Replace gts with eslint
This commit is contained in:
Родитель
f81123de5a
Коммит
afdd0dad0f
|
@ -0,0 +1,7 @@
|
|||
# don't ever lint node_modules
|
||||
node_modules
|
||||
# don't lint build output (make sure it's set to your correct build folder name)
|
||||
out
|
||||
assets
|
||||
views
|
||||
vendor/node-usb-native
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"mocha": true,
|
||||
"amd": true,
|
||||
"commonjs": true
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint/eslint-plugin"
|
||||
],
|
||||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"no-dupe-class-members": "off",
|
||||
"@typescript-eslint/no-empty-function": ["error", { "allow": ["constructors"] }],
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"indent": ["error", 2],
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
// enable the rule specifically for TypeScript files
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-function-return-type": ["error"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -6,5 +6,25 @@
|
|||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
"eslint.autoFixOnSave": true,
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
{"language": "typescript", "autoFix": true },
|
||||
{"language": "typescriptreact", "autoFix": true }
|
||||
],
|
||||
"editor.formatOnSave": true,
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": false,
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.formatOnSave": false,
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": false,
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.formatOnSave": false,
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ function command(cmd, callback) {
|
|||
if (!cmd) {
|
||||
return;
|
||||
}
|
||||
let args = Array.from(arguments);
|
||||
const args = Array.from(arguments);
|
||||
if (typeof args[args.length - 1] === 'function') {
|
||||
callback = args[args.length - 1];
|
||||
args.length = args.length - 1;
|
||||
|
|
|
@ -4,7 +4,7 @@ const IS_PUBLIC_URL = "?public";
|
|||
const VALUE = "value";
|
||||
const ALL = "All";
|
||||
|
||||
var repository = new Vue({
|
||||
const repository = new Vue({
|
||||
el: "#main",
|
||||
data: {
|
||||
companyName: _location.search === IS_PUBLIC_URL ? "Public repository" : "Company repository",
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1
gts.cmd
1
gts.cmd
|
@ -1 +0,0 @@
|
|||
node node_modules/gts/build/src/cli.js %*
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
23
package.json
23
package.json
|
@ -229,9 +229,8 @@
|
|||
"watch": "tsc -watch -p ./ && node scripts/copyVendor.js",
|
||||
"postinstall": "node ./node_modules/vscode/bin/install",
|
||||
"test": "npm run compile && node ./node_modules/vscode/bin/test",
|
||||
"check": "gts check",
|
||||
"clean": "gts clean",
|
||||
"fix": "gts fix",
|
||||
"check": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||
"fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
|
||||
"prepare": "npm run compile",
|
||||
"pretest": "npm run compile",
|
||||
"posttest": "npm run check",
|
||||
|
@ -241,26 +240,28 @@
|
|||
"@types/adm-zip": "^0.4.31",
|
||||
"@types/crc": "^3.4.0",
|
||||
"@types/extract-zip": "^1.6.2",
|
||||
"@types/fs-extra": "^7.0.0",
|
||||
"@types/fs-plus": "^3.0.0",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/lodash.filter": "^4.6.6",
|
||||
"@types/lodash.foreach": "^4.5.6",
|
||||
"@types/lodash.trimstart": "^4.5.6",
|
||||
"@types/lodash.uniq": "^4.5.4",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/node": "^10.14.20",
|
||||
"@types/node": "^10.17.13",
|
||||
"@types/opn": "3.0.28",
|
||||
"@types/request-promise": "^4.1.44",
|
||||
"@types/ssh2": "^0.5.39",
|
||||
"@types/unzip": "^0.1.0",
|
||||
"@types/winreg": "^1.2.30",
|
||||
"@types/request-promise": "^4.1.44",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.15.0",
|
||||
"@typescript-eslint/parser": "^2.15.0",
|
||||
"eslint": "^6.8.0",
|
||||
"glob": "^7.1.4",
|
||||
"gts": "^0.5.4",
|
||||
"typescript": "^2.6.2",
|
||||
"typescript": "^3.2.1",
|
||||
"vscode": "^1.1.33",
|
||||
"webpack": "^4.41.0",
|
||||
"webpack-cli": "^3.3.9",
|
||||
"@types/fs-extra": "^7.0.0"
|
||||
"webpack": "^4.41.5",
|
||||
"webpack-cli": "^3.3.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.4.11",
|
||||
|
|
|
@ -1,34 +1,5 @@
|
|||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* If Nightly Build or Production release or RC release, modify package.json and related template files.
|
||||
* If common PR, do no modification.
|
||||
* TRAVIS_EVENT_TYPE === "cron" : Nightly Build
|
||||
* TRAVIS_TAG =~ /^v?[0-9]+\.[0-9]+\.[0-9]+$/: Production release (eg. v0.10.18)
|
||||
* TRAVIS_TAG =~ /^v?[0-9]+\.[0-9]+\.[0-9]+-[rR][cC]/: RC release (eg. v0.10.18-rc, v0.10.18-rc2, etc.)
|
||||
*/
|
||||
const packageJson = JSON.parse(fs.readFileSync('package.json'));
|
||||
|
||||
// Nightly Build
|
||||
if (process.env.BUILD_REASON === "Schedule") {
|
||||
const nightlyBuildName = "test-owl-project-nightly";
|
||||
const nightlyBuildDisplayName = "Test OWL Project (Nightly)";
|
||||
updateConfigForNonProduction(packageJson, nightlyBuildName, nightlyBuildDisplayName);
|
||||
} else if (process.env.IS_PROD) {
|
||||
// Update resource link
|
||||
const codeGenUrl = "https://aka.ms/iot-codegen-cli-for-workbench";
|
||||
packageJson.codeGenConfigUrl = codeGenUrl;
|
||||
|
||||
// Update production AI Key
|
||||
packageJson.aiKey = process.env.PROD_AIKEY;
|
||||
} else if (process.env.IS_TEST) {
|
||||
const testName = "test-owl-project";
|
||||
const testDisplayName = "Test OWL Project RC";
|
||||
updateConfigForNonProduction(packageJson, testName, testDisplayName);
|
||||
}
|
||||
|
||||
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2) + '\n');
|
||||
|
||||
/**
|
||||
* Update package.json with test name, displayName, publisher, ai aky.
|
||||
* Trim version number. Delete extension icon.
|
||||
|
@ -65,4 +36,32 @@ function updateConfigForNonProduction(packageJson, testName, testDisplayName) {
|
|||
const replaceJson = originalJsonFile.replace(extensionIdPattern, testExtensionId.toLowerCase());
|
||||
fs.writeFileSync(filePath, replaceJson);
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If Nightly Build or Production release or RC release, modify package.json and related template files.
|
||||
* If common PR, do no modification.
|
||||
* TRAVIS_EVENT_TYPE === "cron" : Nightly Build
|
||||
* TRAVIS_TAG =~ /^v?[0-9]+\.[0-9]+\.[0-9]+$/: Production release (eg. v0.10.18)
|
||||
* TRAVIS_TAG =~ /^v?[0-9]+\.[0-9]+\.[0-9]+-[rR][cC]/: RC release (eg. v0.10.18-rc, v0.10.18-rc2, etc.)
|
||||
*/
|
||||
const packageJson = JSON.parse(fs.readFileSync('package.json'));
|
||||
|
||||
// Nightly Build
|
||||
if (process.env.BUILD_REASON === "Schedule") {
|
||||
const nightlyBuildName = "test-owl-project-nightly";
|
||||
const nightlyBuildDisplayName = "Test OWL Project (Nightly)";
|
||||
updateConfigForNonProduction(packageJson, nightlyBuildName, nightlyBuildDisplayName);
|
||||
} else if (process.env.IS_PROD) {
|
||||
// Update resource link
|
||||
const codeGenUrl = "https://aka.ms/iot-codegen-cli-for-workbench";
|
||||
packageJson.codeGenConfigUrl = codeGenUrl;
|
||||
|
||||
// Update production AI Key
|
||||
packageJson.aiKey = process.env.PROD_AIKEY;
|
||||
} else if (process.env.IS_TEST) {
|
||||
const testName = "test-owl-project";
|
||||
const testDisplayName = "Test OWL Project RC";
|
||||
updateConfigForNonProduction(packageJson, testName, testDisplayName);
|
||||
}
|
||||
|
||||
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2) + '\n');
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {ArduinoCommands} from './common/Commands';
|
||||
import {Board, BoardInstallation} from './Models/Interfaces/Board';
|
||||
import { ArduinoCommands } from './common/Commands';
|
||||
import { Board, BoardInstallation } from './Models/Interfaces/Board';
|
||||
|
||||
export class ArduinoPackageManager {
|
||||
private static INSTALLED_BOARDS: Board[] = [];
|
||||
private static async setAdditionalUrl(url: string) {
|
||||
private static async setAdditionalUrl(url: string): Promise<void> {
|
||||
const existedUrls =
|
||||
vscode.workspace.getConfiguration().get<string[]|string>(
|
||||
'arduino.additionalUrls');
|
||||
'arduino.additionalUrls');
|
||||
if (!existedUrls || existedUrls.length === 0) {
|
||||
await vscode.workspace.getConfiguration().update(
|
||||
'arduino.additionalUrls', url, vscode.ConfigurationTarget.Global);
|
||||
'arduino.additionalUrls', url, vscode.ConfigurationTarget.Global);
|
||||
} else {
|
||||
let _existedUrls: string[];
|
||||
if (typeof existedUrls === 'string') {
|
||||
|
@ -30,17 +30,17 @@ export class ArduinoPackageManager {
|
|||
_existedUrls.push(url);
|
||||
if (typeof existedUrls === 'string') {
|
||||
await vscode.workspace.getConfiguration().update(
|
||||
'arduino.additionalUrls', _existedUrls.join(','),
|
||||
vscode.ConfigurationTarget.Global);
|
||||
'arduino.additionalUrls', _existedUrls.join(','),
|
||||
vscode.ConfigurationTarget.Global);
|
||||
} else {
|
||||
await vscode.workspace.getConfiguration().update(
|
||||
'arduino.additionalUrls', _existedUrls,
|
||||
vscode.ConfigurationTarget.Global);
|
||||
'arduino.additionalUrls', _existedUrls,
|
||||
vscode.ConfigurationTarget.Global);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async installBoard(board: Board) {
|
||||
static async installBoard(board: Board): Promise<void> {
|
||||
if (!board || !board.installation) {
|
||||
return;
|
||||
}
|
||||
|
@ -58,10 +58,10 @@ export class ArduinoPackageManager {
|
|||
|
||||
try {
|
||||
await ArduinoPackageManager.setAdditionalUrl(
|
||||
board.installation.additionalUrl);
|
||||
board.installation.additionalUrl);
|
||||
await vscode.commands.executeCommand(
|
||||
ArduinoCommands.InstallBoard, board.installation.packageName,
|
||||
board.installation.architecture);
|
||||
ArduinoCommands.InstallBoard, board.installation.packageName,
|
||||
board.installation.architecture);
|
||||
ArduinoPackageManager.INSTALLED_BOARDS.push(board);
|
||||
} catch (ignore) {
|
||||
// If we failed to install board package,
|
||||
|
|
|
@ -5,16 +5,12 @@
|
|||
// The module 'vscode' contains the VS Code extensibility API
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
import * as vscode from 'vscode';
|
||||
import {TelemetryContext} from './telemetry';
|
||||
import {constructAndLoadIoTProject} from './utils';
|
||||
import {RemoteExtension} from './Models/RemoteExtension';
|
||||
import {CancelOperationError} from './CancelOperationError';
|
||||
|
||||
import { TelemetryContext } from './telemetry';
|
||||
import { constructAndLoadIoTProject } from './utils';
|
||||
import { RemoteExtension } from './Models/RemoteExtension';
|
||||
|
||||
export class AzureOperator {
|
||||
async provision(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
async provision(context: vscode.ExtensionContext, channel: vscode.OutputChannel, telemetryContext: TelemetryContext): Promise<void> {
|
||||
const iotProject =
|
||||
await constructAndLoadIoTProject(context, channel, telemetryContext);
|
||||
if (!iotProject) {
|
||||
|
@ -26,9 +22,7 @@ export class AzureOperator {
|
|||
}
|
||||
}
|
||||
|
||||
async deploy(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
async deploy(context: vscode.ExtensionContext, channel: vscode.OutputChannel, telemetryContext: TelemetryContext): Promise<void> {
|
||||
// Azure deploy command can be executed only in local environment
|
||||
const isLocal = RemoteExtension.checkLocalBeforeRunCommand(context);
|
||||
if (!isLocal) {
|
||||
|
|
|
@ -6,13 +6,11 @@
|
|||
// Import the module and reference it with the alias vscode in your code below
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {TelemetryContext} from './telemetry';
|
||||
import {constructAndLoadIoTProject} from './utils';
|
||||
import { TelemetryContext } from './telemetry';
|
||||
import { constructAndLoadIoTProject } from './utils';
|
||||
|
||||
export class DeviceOperator {
|
||||
async compile(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
async compile(context: vscode.ExtensionContext, channel: vscode.OutputChannel, telemetryContext: TelemetryContext): Promise<void> {
|
||||
const iotProject =
|
||||
await constructAndLoadIoTProject(context, channel, telemetryContext);
|
||||
if (!iotProject) {
|
||||
|
@ -21,9 +19,7 @@ export class DeviceOperator {
|
|||
await iotProject.compile();
|
||||
}
|
||||
|
||||
async upload(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
async upload(context: vscode.ExtensionContext, channel: vscode.OutputChannel, telemetryContext: TelemetryContext): Promise<void> {
|
||||
const iotProject =
|
||||
await constructAndLoadIoTProject(context, channel, telemetryContext);
|
||||
if (!iotProject) {
|
||||
|
@ -32,9 +28,7 @@ export class DeviceOperator {
|
|||
await iotProject.upload();
|
||||
}
|
||||
|
||||
async configDeviceSettings(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
async configDeviceSettings(context: vscode.ExtensionContext, channel: vscode.OutputChannel, telemetryContext: TelemetryContext): Promise<void> {
|
||||
const iotProject =
|
||||
await constructAndLoadIoTProject(context, channel, telemetryContext);
|
||||
if (!iotProject) {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import {MessageItem} from 'vscode';
|
||||
import { MessageItem } from 'vscode';
|
||||
|
||||
export class DialogResponses {
|
||||
static skipForNow: MessageItem = {title: 'Skip for now'};
|
||||
static all: MessageItem = {title: 'All'};
|
||||
static yes: MessageItem = {title: 'Yes'};
|
||||
static no: MessageItem = {title: 'No'};
|
||||
static cancel: MessageItem = {title: 'Cancel', isCloseAffordance: true};
|
||||
static skipForNow: MessageItem = { title: 'Skip for now' };
|
||||
static all: MessageItem = { title: 'All' };
|
||||
static yes: MessageItem = { title: 'Yes' };
|
||||
static no: MessageItem = { title: 'No' };
|
||||
static cancel: MessageItem = { title: 'Cancel', isCloseAffordance: true };
|
||||
}
|
|
@ -11,20 +11,20 @@ import * as path from 'path';
|
|||
import request = require('request-promise');
|
||||
|
||||
import * as utils from '../utils';
|
||||
import {FileNames, ConfigKey, ScaffoldType, OSPlatform} from '../constants';
|
||||
import {TelemetryContext} from '../telemetry';
|
||||
import {DigitalTwinConstants} from './DigitalTwinConstants';
|
||||
import {CodeGenProjectType, DeviceConnectionType, CodeGenLanguage, DeviceSdkReferenceType, CodeGenExecutionItem} from './DigitalTwinCodeGen/Interfaces/CodeGenerator';
|
||||
import {AnsiCCodeGeneratorFactory} from './DigitalTwinCodeGen/AnsiCCodeGeneratorFactory';
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {PnpDeviceConnection, CodeGenProjectTemplate, DeviceSdkReference} from '../Models/Interfaces/ProjectTemplate';
|
||||
import {DialogResponses} from '../DialogResponses';
|
||||
import {RemoteExtension} from '../Models/RemoteExtension';
|
||||
import {DigitalTwinUtility} from './DigitalTwinUtility';
|
||||
import {CodeGenUtility} from './DigitalTwinCodeGen/CodeGenUtility';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {CancelOperationError} from '../CancelOperationError';
|
||||
import {Utility} from './pnp/src/common/utility';
|
||||
import { FileNames, ConfigKey, ScaffoldType, OSPlatform } from '../constants';
|
||||
import { TelemetryContext } from '../telemetry';
|
||||
import { DigitalTwinConstants } from './DigitalTwinConstants';
|
||||
import { CodeGenProjectType, DeviceConnectionType, CodeGenLanguage, DeviceSdkReferenceType, CodeGenExecutionItem } from './DigitalTwinCodeGen/Interfaces/CodeGenerator';
|
||||
import { AnsiCCodeGeneratorFactory } from './DigitalTwinCodeGen/AnsiCCodeGeneratorFactory';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { PnpDeviceConnection, CodeGenProjectTemplate, DeviceSdkReference } from '../Models/Interfaces/ProjectTemplate';
|
||||
import { DialogResponses } from '../DialogResponses';
|
||||
import { RemoteExtension } from '../Models/RemoteExtension';
|
||||
import { DigitalTwinUtility } from './DigitalTwinUtility';
|
||||
import { CodeGenUtility } from './DigitalTwinCodeGen/CodeGenUtility';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { CancelOperationError } from '../CancelOperationError';
|
||||
import { Utility } from './pnp/src/common/utility';
|
||||
|
||||
interface CodeGeneratorDownloadLocation {
|
||||
win32Md5: string;
|
||||
|
@ -60,10 +60,31 @@ enum ReGenResult {
|
|||
Skipped
|
||||
}
|
||||
|
||||
function compareVersion(verion1: string, verion2: string): 1 | -1 | 0 {
|
||||
const ver1 = verion1.split('.');
|
||||
const ver2 = verion2.split('.');
|
||||
let i = 0;
|
||||
let v1: number, v2: number;
|
||||
|
||||
/* default is 0, version format should be 1.1.0 */
|
||||
while (i < 3) {
|
||||
v1 = Number(ver1[i]);
|
||||
v2 = Number(ver2[i]);
|
||||
if (v1 > v2) return 1;
|
||||
if (v1 < v2) return -1;
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function localCodeGenCliPath(): string {
|
||||
return path.join(os.homedir(), DigitalTwinConstants.codeGenCliFolder);
|
||||
}
|
||||
|
||||
export class CodeGeneratorCore {
|
||||
async generateDeviceCodeStub(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> {
|
||||
const isLocal = RemoteExtension.checkLocalBeforeRunCommand(context);
|
||||
if (!isLocal) {
|
||||
return;
|
||||
|
@ -86,7 +107,7 @@ export class CodeGeneratorCore {
|
|||
|
||||
// Prompt if old project exists for the same Capability Model file
|
||||
const regenResult = await this.regenCode(
|
||||
rootPath, capabilityModelFilePath, context, channel, telemetryContext);
|
||||
rootPath, capabilityModelFilePath, context, channel, telemetryContext);
|
||||
if (regenResult === ReGenResult.Succeeded) {
|
||||
return;
|
||||
}
|
||||
|
@ -107,15 +128,15 @@ export class CodeGeneratorCore {
|
|||
|
||||
// Select project template
|
||||
const codeGenProjectType = await this.selectProjectTemplate(
|
||||
channel, codeGenLanguage, codeGenOptions);
|
||||
channel, codeGenLanguage, codeGenOptions);
|
||||
|
||||
// Select Device SDK reference type for CMake project
|
||||
const sdkReferenceType = await this.selectDeviceSdkReferenceType(
|
||||
channel, codeGenProjectType, codeGenOptions);
|
||||
channel, codeGenProjectType, codeGenOptions);
|
||||
|
||||
// Download dependent interface of capability model
|
||||
if (!await DigitalTwinUtility.downloadDependentInterface(
|
||||
rootPath, capabilityModelFilePath)) {
|
||||
rootPath, capabilityModelFilePath)) {
|
||||
throw new Error(`Failed to download dependent interface.`);
|
||||
}
|
||||
|
||||
|
@ -132,19 +153,19 @@ export class CodeGeneratorCore {
|
|||
|
||||
const scaffoldType = ScaffoldType.Local;
|
||||
await this.saveCodeGenConfig(
|
||||
scaffoldType, rootPath, capabilityModelFilePath, codeGenExecutionInfo);
|
||||
scaffoldType, rootPath, capabilityModelFilePath, codeGenExecutionInfo);
|
||||
|
||||
await this.generateDeviceCodeCore(
|
||||
codeGenExecutionInfo, context, channel, telemetryContext);
|
||||
codeGenExecutionInfo, context, channel, telemetryContext);
|
||||
}
|
||||
|
||||
private async regenCode(
|
||||
rootPath: string, capabilityModelFilePath: string,
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<ReGenResult> {
|
||||
rootPath: string, capabilityModelFilePath: string,
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<ReGenResult> {
|
||||
const codeGenConfigPath = path.join(
|
||||
rootPath, FileNames.vscodeSettingsFolderName,
|
||||
DigitalTwinConstants.codeGenConfigFileName);
|
||||
rootPath, FileNames.vscodeSettingsFolderName,
|
||||
DigitalTwinConstants.codeGenConfigFileName);
|
||||
|
||||
let codeGenExecutionItem: CodeGenExecutionItem|undefined;
|
||||
|
||||
|
@ -158,7 +179,7 @@ export class CodeGeneratorCore {
|
|||
JSON.parse(fs.readFileSync(codeGenConfigPath, 'utf8'));
|
||||
if (codeGenExecutions) {
|
||||
codeGenExecutionItem = codeGenExecutions.codeGenExecutionItems.find(
|
||||
item => item.capabilityModelFilePath === capabilityModelFilePath);
|
||||
item => item.capabilityModelFilePath === capabilityModelFilePath);
|
||||
}
|
||||
} catch {
|
||||
// just skip this if read file failed.
|
||||
|
@ -168,15 +189,15 @@ export class CodeGeneratorCore {
|
|||
const regenOptions: vscode.QuickPickItem[] = [];
|
||||
// select the target of the code stub
|
||||
regenOptions.push(
|
||||
{
|
||||
label: `Re-generate code for ${codeGenExecutionItem.projectName}`,
|
||||
description: ''
|
||||
},
|
||||
{label: 'Create new project', description: ''});
|
||||
{
|
||||
label: `Re-generate code for ${codeGenExecutionItem.projectName}`,
|
||||
description: ''
|
||||
},
|
||||
{ label: 'Create new project', description: '' });
|
||||
|
||||
const regenSelection = await vscode.window.showQuickPick(
|
||||
regenOptions,
|
||||
{ignoreFocusOut: true, placeHolder: 'Please select an option:'});
|
||||
regenOptions,
|
||||
{ ignoreFocusOut: true, placeHolder: 'Please select an option:' });
|
||||
|
||||
if (!regenSelection) {
|
||||
throw new CancelOperationError('Re-generate code selection cancelled.');
|
||||
|
@ -185,19 +206,19 @@ export class CodeGeneratorCore {
|
|||
// User select regenerate code
|
||||
if (regenSelection.label !== 'Create new project') {
|
||||
if (!await DigitalTwinUtility.downloadDependentInterface(
|
||||
rootPath, capabilityModelFilePath)) {
|
||||
rootPath, capabilityModelFilePath)) {
|
||||
return ReGenResult.Skipped;
|
||||
}
|
||||
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`${
|
||||
DigitalTwinConstants
|
||||
.dtPrefix} Regenerate device code using an existing CodeGen configure:`);
|
||||
channel,
|
||||
`${
|
||||
DigitalTwinConstants
|
||||
.dtPrefix} Regenerate device code using an existing CodeGen configure:`);
|
||||
CodeGenUtility.printCodeGenConfig(codeGenExecutionItem, channel);
|
||||
|
||||
await this.generateDeviceCodeCore(
|
||||
codeGenExecutionItem, context, channel, telemetryContext);
|
||||
codeGenExecutionItem, context, channel, telemetryContext);
|
||||
return ReGenResult.Succeeded;
|
||||
} else {
|
||||
return ReGenResult.Skipped;
|
||||
|
@ -208,7 +229,7 @@ export class CodeGeneratorCore {
|
|||
}
|
||||
|
||||
private async getCodeGenProjectName(
|
||||
channel: vscode.OutputChannel, rootPath: string): Promise<string> {
|
||||
channel: vscode.OutputChannel, rootPath: string): Promise<string> {
|
||||
// select the project name for code gen
|
||||
const codeGenProjectName = await vscode.window.showInputBox({
|
||||
placeHolder: 'Please input the project name here.',
|
||||
|
@ -219,7 +240,7 @@ export class CodeGeneratorCore {
|
|||
}
|
||||
if (!DigitalTwinConstants.codegenProjectNameRegex.test(projectName)) {
|
||||
return `Project name can only contain ${
|
||||
DigitalTwinConstants.codegenProjectNameRegexDescription}.`;
|
||||
DigitalTwinConstants.codegenProjectNameRegexDescription}.`;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -227,41 +248,41 @@ export class CodeGeneratorCore {
|
|||
|
||||
if (!codeGenProjectName) {
|
||||
throw new CancelOperationError(
|
||||
`Project name is not specified, cancelled.`);
|
||||
`Project name is not specified, cancelled.`);
|
||||
}
|
||||
|
||||
const projectPath = path.join(rootPath, codeGenProjectName);
|
||||
|
||||
if (fs.isDirectorySync(projectPath)) {
|
||||
const messge = `The folder ${
|
||||
projectPath} already exists. Do you want to overwrite the contents in this folder?`;
|
||||
projectPath} already exists. Do you want to overwrite the contents in this folder?`;
|
||||
const choice = await vscode.window.showWarningMessage(
|
||||
messge, DialogResponses.yes, DialogResponses.no);
|
||||
messge, DialogResponses.yes, DialogResponses.no);
|
||||
if (choice !== DialogResponses.yes) {
|
||||
throw new CancelOperationError(
|
||||
`Valid project name is not specified, cancelled.`);
|
||||
`Valid project name is not specified, cancelled.`);
|
||||
}
|
||||
}
|
||||
|
||||
utils.channelShowAndAppendLine(
|
||||
channel, `Input project name: ${codeGenProjectName}`);
|
||||
channel, `Input project name: ${codeGenProjectName}`);
|
||||
return codeGenProjectName;
|
||||
}
|
||||
|
||||
private readCodeGenOptionsConfiguration(
|
||||
// tslint:disable-next-line: no-any
|
||||
context: vscode.ExtensionContext): any {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
context: vscode.ExtensionContext): any {
|
||||
// Load CodeGen configuration file which defines the available CodeGen
|
||||
// options in VS Code command palette
|
||||
const codeGenConfigFilePath: string = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.codeGenOptionsFileName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.codeGenOptionsFileName));
|
||||
return JSON.parse(fs.readFileSync(codeGenConfigFilePath, 'utf8'));
|
||||
}
|
||||
|
||||
private async selectLanguage(channel: vscode.OutputChannel): Promise<string> {
|
||||
const languageItems: vscode.QuickPickItem[] = [];
|
||||
languageItems.push({label: CodeGenLanguage.ANSIC, description: ''});
|
||||
languageItems.push({ label: CodeGenLanguage.ANSIC, description: '' });
|
||||
|
||||
const languageSelection = await vscode.window.showQuickPick(languageItems, {
|
||||
ignoreFocusOut: true,
|
||||
|
@ -273,21 +294,21 @@ export class CodeGeneratorCore {
|
|||
}
|
||||
|
||||
utils.channelShowAndAppendLine(
|
||||
channel, `Selected CodeGen language: ${languageSelection.label}`);
|
||||
channel, `Selected CodeGen language: ${languageSelection.label}`);
|
||||
return languageSelection.label;
|
||||
}
|
||||
|
||||
private async selectConnectionType(
|
||||
channel: vscode.OutputChannel,
|
||||
// tslint:disable-next-line: no-any
|
||||
codegenOptionsConfig: any): Promise<DeviceConnectionType> {
|
||||
channel: vscode.OutputChannel,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
codegenOptionsConfig: any): Promise<DeviceConnectionType> {
|
||||
// Load available Azure IoT connection types from JSON configuration
|
||||
const connectionTypeItems: vscode.QuickPickItem[] = [];
|
||||
codegenOptionsConfig.connectionTypes.forEach(
|
||||
(element: PnpDeviceConnection) => {
|
||||
connectionTypeItems.push(
|
||||
{label: element.name, detail: element.detail});
|
||||
});
|
||||
(element: PnpDeviceConnection) => {
|
||||
connectionTypeItems.push(
|
||||
{ label: element.name, detail: element.detail });
|
||||
});
|
||||
|
||||
const deviceConnectionSelection =
|
||||
await vscode.window.showQuickPick(connectionTypeItems, {
|
||||
|
@ -300,53 +321,52 @@ export class CodeGeneratorCore {
|
|||
}
|
||||
|
||||
const deviceConnection = codegenOptionsConfig.connectionTypes.find(
|
||||
(connectionType: PnpDeviceConnection) => {
|
||||
return connectionType.name === deviceConnectionSelection.label;
|
||||
});
|
||||
(connectionType: PnpDeviceConnection) => {
|
||||
return connectionType.name === deviceConnectionSelection.label;
|
||||
});
|
||||
|
||||
const connectionType: DeviceConnectionType = DeviceConnectionType
|
||||
[deviceConnection.type as keyof typeof DeviceConnectionType];
|
||||
const connectionType: DeviceConnectionType = DeviceConnectionType[deviceConnection.type as keyof typeof DeviceConnectionType];
|
||||
|
||||
if (!connectionType) {
|
||||
throw new Error(
|
||||
`Failed to find an available device connection type with selection label '${
|
||||
deviceConnectionSelection.label}' from CodeGen configuration.`);
|
||||
`Failed to find an available device connection type with selection label '${
|
||||
deviceConnectionSelection.label}' from CodeGen configuration.`);
|
||||
}
|
||||
|
||||
utils.channelShowAndAppendLine(
|
||||
channel, `Selected device connection type: ${connectionType}`);
|
||||
channel, `Selected device connection type: ${connectionType}`);
|
||||
return connectionType;
|
||||
}
|
||||
|
||||
private async selectProjectTemplate(
|
||||
channel: vscode.OutputChannel, language: string,
|
||||
// tslint:disable-next-line: no-any
|
||||
codegenOptionsConfig: any): Promise<CodeGenProjectType> {
|
||||
channel: vscode.OutputChannel, language: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
codegenOptionsConfig: any): Promise<CodeGenProjectType> {
|
||||
// Load available project templates from JSON configuration
|
||||
const projectTemplates = codegenOptionsConfig.projectTemplates.filter(
|
||||
(projectTemplate: CodeGenProjectTemplate) => {
|
||||
return (
|
||||
projectTemplate.enabled && projectTemplate.language === language);
|
||||
});
|
||||
(projectTemplate: CodeGenProjectTemplate) => {
|
||||
return (
|
||||
projectTemplate.enabled && projectTemplate.language === language);
|
||||
});
|
||||
|
||||
const projectTemplateItems: vscode.QuickPickItem[] = [];
|
||||
projectTemplates.forEach((element: CodeGenProjectTemplate) => {
|
||||
projectTemplateItems.push({label: element.name, detail: element.detail});
|
||||
projectTemplateItems.push({ label: element.name, detail: element.detail });
|
||||
});
|
||||
|
||||
if (!projectTemplateItems) {
|
||||
throw new Error(
|
||||
`Internal error. Unable to find available project templates using ${
|
||||
language} language.`);
|
||||
`Internal error. Unable to find available project templates using ${
|
||||
language} language.`);
|
||||
}
|
||||
|
||||
const projectTemplateSelection = await vscode.window.showQuickPick(
|
||||
projectTemplateItems,
|
||||
{ignoreFocusOut: true, placeHolder: 'Select project template:'});
|
||||
projectTemplateItems,
|
||||
{ ignoreFocusOut: true, placeHolder: 'Select project template:' });
|
||||
|
||||
if (!projectTemplateSelection) {
|
||||
throw new CancelOperationError(
|
||||
'CodeGen project template selection cancelled.');
|
||||
'CodeGen project template selection cancelled.');
|
||||
}
|
||||
|
||||
const projectTemplate =
|
||||
|
@ -354,119 +374,117 @@ export class CodeGeneratorCore {
|
|||
return projectType.name === projectTemplateSelection.label;
|
||||
});
|
||||
|
||||
const codeGenProjectType: CodeGenProjectType = CodeGenProjectType
|
||||
[projectTemplate.type as keyof typeof CodeGenProjectType];
|
||||
const codeGenProjectType: CodeGenProjectType = CodeGenProjectType[projectTemplate.type as keyof typeof CodeGenProjectType];
|
||||
|
||||
if (!codeGenProjectType) {
|
||||
throw new Error(
|
||||
`Failed to find an available project template with selection label '${
|
||||
projectTemplateSelection.label}' from CodeGen configuration.`);
|
||||
`Failed to find an available project template with selection label '${
|
||||
projectTemplateSelection.label}' from CodeGen configuration.`);
|
||||
}
|
||||
|
||||
utils.channelShowAndAppendLine(
|
||||
channel, `Selected CodeGen project type: ${codeGenProjectType}`);
|
||||
channel, `Selected CodeGen project type: ${codeGenProjectType}`);
|
||||
return codeGenProjectType;
|
||||
}
|
||||
|
||||
private async selectDeviceSdkReferenceType(
|
||||
channel: vscode.OutputChannel, projectType: CodeGenProjectType,
|
||||
// tslint:disable-next-line: no-any
|
||||
codegenOptionsConfig: any): Promise<DeviceSdkReferenceType> {
|
||||
channel: vscode.OutputChannel, projectType: CodeGenProjectType,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
codegenOptionsConfig: any): Promise<DeviceSdkReferenceType> {
|
||||
let deviceSdkReferenceType = undefined;
|
||||
switch (projectType) {
|
||||
case CodeGenProjectType.IoTDevKit:
|
||||
deviceSdkReferenceType = DeviceSdkReferenceType.DevKitSDK;
|
||||
break;
|
||||
case CodeGenProjectType.CMakeWindows:
|
||||
case CodeGenProjectType.CMakeLinux: {
|
||||
// Load available Azure IoT connection types from JSON configuration
|
||||
const sdkReferenceTypeItems: vscode.QuickPickItem[] = [];
|
||||
codegenOptionsConfig.deviceSdkReferenceTypes.forEach(
|
||||
(element: DeviceSdkReference) => {
|
||||
sdkReferenceTypeItems.push(
|
||||
{label: element.name, detail: element.detail});
|
||||
});
|
||||
case CodeGenProjectType.IoTDevKit:
|
||||
deviceSdkReferenceType = DeviceSdkReferenceType.DevKitSDK;
|
||||
break;
|
||||
case CodeGenProjectType.CMakeWindows:
|
||||
case CodeGenProjectType.CMakeLinux: {
|
||||
// Load available Azure IoT connection types from JSON configuration
|
||||
const sdkReferenceTypeItems: vscode.QuickPickItem[] = [];
|
||||
codegenOptionsConfig.deviceSdkReferenceTypes.forEach(
|
||||
(element: DeviceSdkReference) => {
|
||||
sdkReferenceTypeItems.push(
|
||||
{ label: element.name, detail: element.detail });
|
||||
});
|
||||
|
||||
const deviceConnectionSelection =
|
||||
const deviceConnectionSelection =
|
||||
await vscode.window.showQuickPick(sdkReferenceTypeItems, {
|
||||
ignoreFocusOut: true,
|
||||
placeHolder: 'How will CMake include the Azure IoT Device SDK?'
|
||||
});
|
||||
|
||||
if (!deviceConnectionSelection) {
|
||||
throw new CancelOperationError(
|
||||
'IoT Device SDK reference type selection cancelled.');
|
||||
}
|
||||
|
||||
// Map selection to a DeviceSdkReferenceType enum
|
||||
const sdkReference = codegenOptionsConfig.deviceSdkReferenceTypes.find(
|
||||
(sdkReference: DeviceSdkReference) => {
|
||||
return sdkReference.name === deviceConnectionSelection.label;
|
||||
});
|
||||
|
||||
const sdkReferenceType: DeviceSdkReferenceType = DeviceSdkReferenceType
|
||||
[sdkReference.type as keyof typeof DeviceSdkReferenceType];
|
||||
|
||||
if (!sdkReference) {
|
||||
throw new Error(
|
||||
`Failed to find an available SDK reference type with selection label '${
|
||||
deviceConnectionSelection
|
||||
.label}' from CodeGen configuration.`);
|
||||
}
|
||||
|
||||
deviceSdkReferenceType = sdkReferenceType;
|
||||
break;
|
||||
if (!deviceConnectionSelection) {
|
||||
throw new CancelOperationError(
|
||||
'IoT Device SDK reference type selection cancelled.');
|
||||
}
|
||||
default:
|
||||
throw new Error(`projectType ${projectType} is not supported.`);
|
||||
|
||||
// Map selection to a DeviceSdkReferenceType enum
|
||||
const sdkReference = codegenOptionsConfig.deviceSdkReferenceTypes.find(
|
||||
(sdkReference: DeviceSdkReference) => {
|
||||
return sdkReference.name === deviceConnectionSelection.label;
|
||||
});
|
||||
|
||||
const sdkReferenceType: DeviceSdkReferenceType = DeviceSdkReferenceType[sdkReference.type as keyof typeof DeviceSdkReferenceType];
|
||||
|
||||
if (!sdkReference) {
|
||||
throw new Error(
|
||||
`Failed to find an available SDK reference type with selection label '${
|
||||
deviceConnectionSelection
|
||||
.label}' from CodeGen configuration.`);
|
||||
}
|
||||
|
||||
deviceSdkReferenceType = sdkReferenceType;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`projectType ${projectType} is not supported.`);
|
||||
}
|
||||
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`Selected device SDK reference type: ${deviceSdkReferenceType}`);
|
||||
channel,
|
||||
`Selected device SDK reference type: ${deviceSdkReferenceType}`);
|
||||
return deviceSdkReferenceType;
|
||||
}
|
||||
|
||||
private async generateDeviceCodeCore(
|
||||
codeGenExecutionInfo: CodeGenExecutionItem,
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<boolean> {
|
||||
codeGenExecutionInfo: CodeGenExecutionItem,
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<boolean> {
|
||||
// We only support Ansi C
|
||||
const codeGenFactory =
|
||||
new AnsiCCodeGeneratorFactory(context, channel, telemetryContext);
|
||||
|
||||
const codeGenerator = codeGenFactory.createCodeGeneratorImpl(
|
||||
codeGenExecutionInfo.languageLabel);
|
||||
codeGenExecutionInfo.languageLabel);
|
||||
if (!codeGenerator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse capabilityModel name from id
|
||||
const capabilityModel = await Utility.getJsonContent(
|
||||
codeGenExecutionInfo.capabilityModelFilePath);
|
||||
codeGenExecutionInfo.capabilityModelFilePath);
|
||||
const capabilityModelId = capabilityModel['@id'];
|
||||
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: `Generate PnP device code for ${capabilityModelId} ...`
|
||||
},
|
||||
async () => {
|
||||
const result = await codeGenerator.generateCode(codeGenExecutionInfo);
|
||||
if (result) {
|
||||
vscode.window.showInformationMessage(
|
||||
`Generate PnP device code for ${capabilityModelId} completed`);
|
||||
}
|
||||
});
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: `Generate PnP device code for ${capabilityModelId} ...`
|
||||
},
|
||||
async () => {
|
||||
const result = await codeGenerator.generateCode(codeGenExecutionInfo);
|
||||
if (result) {
|
||||
vscode.window.showInformationMessage(
|
||||
`Generate PnP device code for ${capabilityModelId} completed`);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private async saveCodeGenConfig(
|
||||
type: ScaffoldType, rootPath: string, capabilityModelFilePath: string,
|
||||
codeGenExecutionInfo: CodeGenExecutionItem): Promise<void> {
|
||||
type: ScaffoldType, rootPath: string, capabilityModelFilePath: string,
|
||||
codeGenExecutionInfo: CodeGenExecutionItem): Promise<void> {
|
||||
const codeGenConfigPath = path.join(
|
||||
rootPath, FileNames.vscodeSettingsFolderName,
|
||||
DigitalTwinConstants.codeGenConfigFileName);
|
||||
rootPath, FileNames.vscodeSettingsFolderName,
|
||||
DigitalTwinConstants.codeGenConfigFileName);
|
||||
|
||||
try {
|
||||
let codeGenExecutions: CodeGenExecutions;
|
||||
|
@ -478,16 +496,16 @@ export class CodeGeneratorCore {
|
|||
if (codeGenExecutions) {
|
||||
codeGenExecutions.codeGenExecutionItems =
|
||||
codeGenExecutions.codeGenExecutionItems.filter(
|
||||
item =>
|
||||
item.capabilityModelFilePath !== capabilityModelFilePath);
|
||||
item =>
|
||||
item.capabilityModelFilePath !== capabilityModelFilePath);
|
||||
codeGenExecutions.codeGenExecutionItems.push(codeGenExecutionInfo);
|
||||
}
|
||||
} else {
|
||||
codeGenExecutions = {codeGenExecutionItems: [codeGenExecutionInfo]};
|
||||
codeGenExecutions = { codeGenExecutionItems: [codeGenExecutionInfo] };
|
||||
}
|
||||
if (codeGenExecutions) {
|
||||
await FileUtility.writeJsonFile(
|
||||
type, codeGenConfigPath, codeGenExecutions);
|
||||
type, codeGenConfigPath, codeGenExecutions);
|
||||
}
|
||||
} catch (error) {
|
||||
// save config failure should not impact code gen.
|
||||
|
@ -496,8 +514,8 @@ export class CodeGeneratorCore {
|
|||
}
|
||||
|
||||
private async getCodeGenCliPackageInfo(
|
||||
context: vscode.ExtensionContext,
|
||||
channel: vscode.OutputChannel): Promise<CodeGeneratorConfigItem|null> {
|
||||
context: vscode.ExtensionContext,
|
||||
channel: vscode.OutputChannel): Promise<CodeGeneratorConfigItem|null> {
|
||||
const extensionPackage = require(context.asAbsolutePath('./package.json'));
|
||||
const extensionVersion = extensionPackage.version;
|
||||
|
||||
|
@ -514,11 +532,11 @@ export class CodeGeneratorCore {
|
|||
const codeGenConfig: CodeGeneratorConfig = await request(options).promise();
|
||||
if (codeGenConfig) {
|
||||
codeGenConfig.codeGeneratorConfigItems.sort(
|
||||
(configItem1, configItem2) => {
|
||||
return compareVersion(
|
||||
configItem2.codeGeneratorVersion,
|
||||
configItem1.codeGeneratorVersion); // reverse order
|
||||
});
|
||||
(configItem1, configItem2) => {
|
||||
return compareVersion(
|
||||
configItem2.codeGeneratorVersion,
|
||||
configItem1.codeGeneratorVersion); // reverse order
|
||||
});
|
||||
|
||||
// if this is a RC build, always use the latest version of code generator.
|
||||
if (!/^[0-9]+\.[0-9]+\.[0-9]+$/.test(extensionVersion)) {
|
||||
|
@ -526,7 +544,7 @@ export class CodeGeneratorCore {
|
|||
} else {
|
||||
for (const item of codeGenConfig.codeGeneratorConfigItems) {
|
||||
if (compareVersion(
|
||||
extensionVersion, item.iotWorkbenchMinimalVersion) >= 0) {
|
||||
extensionVersion, item.iotWorkbenchMinimalVersion) >= 0) {
|
||||
targetConfigItem = item;
|
||||
break;
|
||||
}
|
||||
|
@ -536,9 +554,9 @@ export class CodeGeneratorCore {
|
|||
|
||||
if (!targetConfigItem) {
|
||||
const message = `${
|
||||
DigitalTwinConstants
|
||||
.dtPrefix} Failed to get download information for ${
|
||||
DigitalTwinConstants.codeGenCli}.`;
|
||||
DigitalTwinConstants
|
||||
.dtPrefix} Failed to get download information for ${
|
||||
DigitalTwinConstants.codeGenCli}.`;
|
||||
utils.channelShowAndAppendLine(channel, message);
|
||||
}
|
||||
|
||||
|
@ -565,8 +583,8 @@ export class CodeGeneratorCore {
|
|||
}
|
||||
|
||||
private async downloadAndInstallCodeGenCli(
|
||||
channel: vscode.OutputChannel, targetConfigItem: CodeGeneratorConfigItem,
|
||||
installOrUpgrade: number, newVersion: string): Promise<boolean> {
|
||||
channel: vscode.OutputChannel, targetConfigItem: CodeGeneratorConfigItem,
|
||||
installOrUpgrade: number, newVersion: string): Promise<boolean> {
|
||||
let packageUri: string;
|
||||
let md5value: string;
|
||||
const platform = os.platform();
|
||||
|
@ -583,11 +601,11 @@ export class CodeGeneratorCore {
|
|||
|
||||
// Download
|
||||
utils.channelShowAndAppend(
|
||||
channel,
|
||||
`Step 1: Downloading ${DigitalTwinConstants.codeGenCli} v${
|
||||
newVersion} package ...`);
|
||||
channel,
|
||||
`Step 1: Downloading ${DigitalTwinConstants.codeGenCli} v${
|
||||
newVersion} package ...`);
|
||||
const downloadOption: request
|
||||
.OptionsWithUri = {method: 'GET', uri: packageUri, encoding: null};
|
||||
.OptionsWithUri = { method: 'GET', uri: packageUri, encoding: null };
|
||||
const zipData = await request(downloadOption).promise();
|
||||
const tempPath = path.join(os.tmpdir(), FileNames.iotworkbenchTempFolder);
|
||||
const filePath = path.join(tempPath, `${md5value}.zip`);
|
||||
|
@ -597,24 +615,24 @@ export class CodeGeneratorCore {
|
|||
// Verify
|
||||
// Validate hash code
|
||||
utils.channelShowAndAppend(
|
||||
channel, 'Step 2: Validating hash code for the package ...');
|
||||
channel, 'Step 2: Validating hash code for the package ...');
|
||||
const hashvalue = await FileUtility.getFileHash(filePath);
|
||||
if (hashvalue !== md5value) {
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`the downloaded ${DigitalTwinConstants.codeGenCli} v${
|
||||
newVersion} package has been corrupted.`);
|
||||
channel,
|
||||
`the downloaded ${DigitalTwinConstants.codeGenCli} v${
|
||||
newVersion} package has been corrupted.`);
|
||||
if (installOrUpgrade === CodeGenCliOperation.Install) {
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`${
|
||||
DigitalTwinConstants
|
||||
.dtPrefix} Abort generating device code stub.`);
|
||||
channel,
|
||||
`${
|
||||
DigitalTwinConstants
|
||||
.dtPrefix} Abort generating device code stub.`);
|
||||
return false;
|
||||
} else {
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
` Abort the installation and continue generating device code stub.`);
|
||||
channel,
|
||||
` Abort the installation and continue generating device code stub.`);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
@ -629,23 +647,23 @@ export class CodeGeneratorCore {
|
|||
|
||||
// Update the config
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.codeGeneratorVersion, newVersion,
|
||||
vscode.ConfigurationTarget.Global);
|
||||
ConfigKey.codeGeneratorVersion, newVersion,
|
||||
vscode.ConfigurationTarget.Global);
|
||||
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`${DigitalTwinConstants.dtPrefix} The ${
|
||||
DigitalTwinConstants.codeGenCli} v${newVersion} is ready to use.`);
|
||||
channel,
|
||||
`${DigitalTwinConstants.dtPrefix} The ${
|
||||
DigitalTwinConstants.codeGenCli} v${newVersion} is ready to use.`);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async installOrUpgradeCodeGenCli(
|
||||
context: vscode.ExtensionContext,
|
||||
channel: vscode.OutputChannel): Promise<boolean> {
|
||||
context: vscode.ExtensionContext,
|
||||
channel: vscode.OutputChannel): Promise<boolean> {
|
||||
utils.channelShowAndAppend(
|
||||
channel,
|
||||
`${DigitalTwinConstants.dtPrefix} Check ${
|
||||
DigitalTwinConstants.codeGenCli} ...`);
|
||||
channel,
|
||||
`${DigitalTwinConstants.dtPrefix} Check ${
|
||||
DigitalTwinConstants.codeGenCli} ...`);
|
||||
const targetConfigItem =
|
||||
await this.getCodeGenCliPackageInfo(context, channel);
|
||||
if (targetConfigItem === null) {
|
||||
|
@ -660,7 +678,7 @@ export class CodeGeneratorCore {
|
|||
} else {
|
||||
// Compare version
|
||||
if (compareVersion(
|
||||
targetConfigItem.codeGeneratorVersion, currentVersion) > 0) {
|
||||
targetConfigItem.codeGeneratorVersion, currentVersion) > 0) {
|
||||
// Upgrade
|
||||
installOrUpgrade = CodeGenCliOperation.Upgrade;
|
||||
}
|
||||
|
@ -668,68 +686,47 @@ export class CodeGeneratorCore {
|
|||
if (installOrUpgrade === CodeGenCliOperation.None) {
|
||||
// Already exists
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`CodeGen CLI v${currentVersion} is installed and ready to use.`);
|
||||
channel,
|
||||
`CodeGen CLI v${currentVersion} is installed and ready to use.`);
|
||||
return true;
|
||||
}
|
||||
|
||||
const newVersion = targetConfigItem.codeGeneratorVersion;
|
||||
const processTitle =
|
||||
(installOrUpgrade === CodeGenCliOperation.Install ?
|
||||
`Installing ${DigitalTwinConstants.codeGenCli}` :
|
||||
`Upgrading ${DigitalTwinConstants.codeGenCli}`);
|
||||
`Installing ${DigitalTwinConstants.codeGenCli}` :
|
||||
`Upgrading ${DigitalTwinConstants.codeGenCli}`);
|
||||
const message =
|
||||
(installOrUpgrade === CodeGenCliOperation.Install ?
|
||||
` not installed, start installing:` :
|
||||
` new version detected, start upgrading from ${
|
||||
currentVersion} to ${newVersion}:`);
|
||||
` not installed, start installing:` :
|
||||
` new version detected, start upgrading from ${
|
||||
currentVersion} to ${newVersion}:`);
|
||||
utils.channelShowAndAppendLine(channel, message);
|
||||
|
||||
// Start donwloading
|
||||
let result = false;
|
||||
await vscode.window.withProgress(
|
||||
{location: vscode.ProgressLocation.Notification, title: processTitle},
|
||||
async (progress) => {
|
||||
progress.report({increment: 0});
|
||||
{ location: vscode.ProgressLocation.Notification, title: processTitle },
|
||||
async (progress) => {
|
||||
progress.report({ increment: 0 });
|
||||
|
||||
setTimeout(() => {
|
||||
progress.report({increment: 10, message: `still going...`});
|
||||
}, 1000);
|
||||
setTimeout(() => {
|
||||
progress.report({ increment: 10, message: `still going...` });
|
||||
}, 1000);
|
||||
|
||||
setTimeout(() => {
|
||||
progress.report({increment: 40, message: `still going...`});
|
||||
}, 5000);
|
||||
setTimeout(() => {
|
||||
progress.report({ increment: 40, message: `still going...` });
|
||||
}, 5000);
|
||||
|
||||
setTimeout(() => {
|
||||
progress.report({increment: 50, message: `almost done...`});
|
||||
}, 10000);
|
||||
setTimeout(() => {
|
||||
progress.report({ increment: 50, message: `almost done...` });
|
||||
}, 10000);
|
||||
|
||||
result = await this.downloadAndInstallCodeGenCli(
|
||||
channel, targetConfigItem as CodeGeneratorConfigItem,
|
||||
installOrUpgrade, newVersion);
|
||||
});
|
||||
result = await this.downloadAndInstallCodeGenCli(
|
||||
channel, targetConfigItem as CodeGeneratorConfigItem,
|
||||
installOrUpgrade, newVersion);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function localCodeGenCliPath(): string {
|
||||
return path.join(os.homedir(), DigitalTwinConstants.codeGenCliFolder);
|
||||
}
|
||||
|
||||
function compareVersion(verion1: string, verion2: string) {
|
||||
const ver1 = verion1.split('.');
|
||||
const ver2 = verion2.split('.');
|
||||
let i = 0;
|
||||
let v1: number, v2: number;
|
||||
|
||||
/* default is 0, version format should be 1.1.0 */
|
||||
while (i < 3) {
|
||||
v1 = Number(ver1[i]);
|
||||
v2 = Number(ver2[i]);
|
||||
if (v1 > v2) return 1;
|
||||
if (v1 < v2) return -1;
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import * as vscode from 'vscode';
|
||||
|
||||
import {TelemetryContext} from '../../telemetry';
|
||||
import {AnsiCCodeGenerator} from './Interfaces/AnsiCCodeGenerator';
|
||||
import {CodeGenerator, CodeGenLanguage} from './Interfaces/CodeGenerator';
|
||||
import {CodeGeneratorFactory} from './Interfaces/CodeGeneratorFactory';
|
||||
import { TelemetryContext } from '../../telemetry';
|
||||
import { AnsiCCodeGenerator } from './Interfaces/AnsiCCodeGenerator';
|
||||
import { CodeGenerator, CodeGenLanguage } from './Interfaces/CodeGenerator';
|
||||
import { CodeGeneratorFactory } from './Interfaces/CodeGeneratorFactory';
|
||||
|
||||
export class AnsiCCodeGeneratorFactory implements CodeGeneratorFactory {
|
||||
constructor(
|
||||
|
@ -13,7 +13,7 @@ export class AnsiCCodeGeneratorFactory implements CodeGeneratorFactory {
|
|||
createCodeGeneratorImpl(language: string): CodeGenerator|null {
|
||||
if (language === CodeGenLanguage.ANSIC.toString()) {
|
||||
return new AnsiCCodeGenerator(
|
||||
this.context, this.channel, this.telemetryContext);
|
||||
this.context, this.channel, this.telemetryContext);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -5,32 +5,29 @@
|
|||
|
||||
import * as utils from '../../utils';
|
||||
import * as vscode from 'vscode';
|
||||
import {CodeGenExecutionItem} from './Interfaces/CodeGenerator';
|
||||
import { CodeGenExecutionItem } from './Interfaces/CodeGenerator';
|
||||
|
||||
export class CodeGenUtility {
|
||||
static printCodeGenConfig(
|
||||
codeGenExecutionItem: CodeGenExecutionItem,
|
||||
channel: vscode.OutputChannel,
|
||||
) {
|
||||
static printCodeGenConfig(codeGenExecutionItem: CodeGenExecutionItem, channel: vscode.OutputChannel): void {
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`Device capability model file: ${
|
||||
codeGenExecutionItem.capabilityModelFilePath}`);
|
||||
channel,
|
||||
`Device capability model file: ${
|
||||
codeGenExecutionItem.capabilityModelFilePath}`);
|
||||
utils.channelShowAndAppendLine(
|
||||
channel, `Project name: ${codeGenExecutionItem.projectName}`);
|
||||
channel, `Project name: ${codeGenExecutionItem.projectName}`);
|
||||
utils.channelShowAndAppendLine(
|
||||
channel, `Language: ${codeGenExecutionItem.languageLabel}`);
|
||||
channel, `Language: ${codeGenExecutionItem.languageLabel}`);
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`Device connection type: ${codeGenExecutionItem.deviceConnectionType}`);
|
||||
channel,
|
||||
`Device connection type: ${codeGenExecutionItem.deviceConnectionType}`);
|
||||
utils.channelShowAndAppendLine(
|
||||
channel, `Project type: ${codeGenExecutionItem.codeGenProjectType}`);
|
||||
channel, `Project type: ${codeGenExecutionItem.codeGenProjectType}`);
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`Device SDK reference type: ${
|
||||
codeGenExecutionItem.deviceSdkReferenceType}`);
|
||||
channel,
|
||||
`Device SDK reference type: ${
|
||||
codeGenExecutionItem.deviceSdkReferenceType}`);
|
||||
utils.channelShowAndAppendLine(
|
||||
channel,
|
||||
`Project output directory: ${codeGenExecutionItem.outputDirectory}`);
|
||||
channel,
|
||||
`Project output directory: ${codeGenExecutionItem.outputDirectory}`);
|
||||
}
|
||||
}
|
|
@ -2,15 +2,15 @@ import * as os from 'os';
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {VscodeCommands} from '../../../common/Commands';
|
||||
import {OSPlatform, ScaffoldType} from '../../../constants';
|
||||
import {OpenScenario} from '../../../Models/IoTWorkbenchProjectBase';
|
||||
import {IoTWorkspaceProject} from '../../../Models/IoTWorkspaceProject';
|
||||
import {TelemetryContext} from '../../../telemetry';
|
||||
import { VscodeCommands } from '../../../common/Commands';
|
||||
import { OSPlatform, ScaffoldType } from '../../../constants';
|
||||
import { OpenScenario } from '../../../Models/IoTWorkbenchProjectBase';
|
||||
import { IoTWorkspaceProject } from '../../../Models/IoTWorkspaceProject';
|
||||
import { TelemetryContext } from '../../../telemetry';
|
||||
import * as utils from '../../../utils';
|
||||
import {DigitalTwinConstants} from '../../DigitalTwinConstants';
|
||||
import { DigitalTwinConstants } from '../../DigitalTwinConstants';
|
||||
|
||||
import {CodeGenerator, CodeGenExecutionItem, CodeGenProjectType} from './CodeGenerator';
|
||||
import { CodeGenerator, CodeGenExecutionItem, CodeGenProjectType } from './CodeGenerator';
|
||||
|
||||
export class AnsiCCodeGenerator implements CodeGenerator {
|
||||
constructor(
|
||||
|
@ -25,20 +25,20 @@ export class AnsiCCodeGenerator implements CodeGenerator {
|
|||
if (codegenSucceeded) {
|
||||
if (codegenInfo.codeGenProjectType === CodeGenProjectType.IoTDevKit) {
|
||||
const project: IoTWorkspaceProject = new IoTWorkspaceProject(
|
||||
this.context, this.channel, this.telemetryContext,
|
||||
codegenInfo.outputDirectory);
|
||||
this.context, this.channel, this.telemetryContext,
|
||||
codegenInfo.outputDirectory);
|
||||
project.openProject(
|
||||
ScaffoldType.Local, true, OpenScenario.createNewProject);
|
||||
ScaffoldType.Local, true, OpenScenario.createNewProject);
|
||||
} else {
|
||||
await vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpenFolder,
|
||||
vscode.Uri.file(codegenInfo.outputDirectory), true);
|
||||
VscodeCommands.VscodeOpenFolder,
|
||||
vscode.Uri.file(codegenInfo.outputDirectory), true);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
vscode.window.showErrorMessage(
|
||||
'Unable to generate code, please check output window for detail.');
|
||||
'Unable to generate code, please check output window for detail.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -66,16 +66,16 @@ export class AnsiCCodeGenerator implements CodeGenerator {
|
|||
}
|
||||
|
||||
const command = `${codeGenCommand} generate -d "${dcmFilePath}" -i "${
|
||||
interfaceDir}" -p "${projectTypeValue}" -c "${
|
||||
connectionTypeValue}" -r "${sdkReferenceTypeValue}" -l ansic -o "${
|
||||
outputDir}" -n "${projectName}"`;
|
||||
interfaceDir}" -p "${projectTypeValue}" -c "${
|
||||
connectionTypeValue}" -r "${sdkReferenceTypeValue}" -l ansic -o "${
|
||||
outputDir}" -n "${projectName}"`;
|
||||
|
||||
let message: string;
|
||||
|
||||
try {
|
||||
await utils.runCommand(command, [], cmdPath, this.channel);
|
||||
message = `${
|
||||
DigitalTwinConstants.dtPrefix} generate PnP device code completed.`;
|
||||
DigitalTwinConstants.dtPrefix} generate PnP device code completed.`;
|
||||
utils.channelShowAndAppendLine(this.channel, message);
|
||||
return true;
|
||||
} catch {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {CodeGenerator, CodeGenProjectType, DeviceConnectionType, DeviceSdkReferenceType} from './CodeGenerator';
|
||||
import { CodeGenerator, CodeGenProjectType, DeviceConnectionType, DeviceSdkReferenceType } from './CodeGenerator';
|
||||
|
||||
|
||||
export interface CodeGeneratorFactory {
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import * as utils from '../utils';
|
||||
import {DigitalTwinConstants} from './DigitalTwinConstants';
|
||||
import {CancelOperationError} from '../CancelOperationError';
|
||||
import {ModelRepositoryManager} from './pnp/src/modelRepository/modelRepositoryManager';
|
||||
import {ApiProvider} from './pnp/src/api/apiProvider';
|
||||
import { DigitalTwinConstants } from './DigitalTwinConstants';
|
||||
import { CancelOperationError } from '../CancelOperationError';
|
||||
import { ModelRepositoryManager } from './pnp/src/modelRepository/modelRepositoryManager';
|
||||
import { ApiProvider } from './pnp/src/api/apiProvider';
|
||||
|
||||
/**
|
||||
* Digital Twin extension utility
|
||||
|
@ -16,7 +16,7 @@ import {ApiProvider} from './pnp/src/api/apiProvider';
|
|||
export class DigitalTwinUtility {
|
||||
private static readonly EXTENSION_NOT_INIT =
|
||||
'Azure Digital Twin extension is not inititalized';
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private static extensionInstance: any;
|
||||
private static channel: vscode.OutputChannel;
|
||||
|
||||
|
@ -25,8 +25,8 @@ export class DigitalTwinUtility {
|
|||
* @param channel output channel
|
||||
*/
|
||||
static init(
|
||||
modelRepositoryManager: ModelRepositoryManager,
|
||||
channel: vscode.OutputChannel): void {
|
||||
modelRepositoryManager: ModelRepositoryManager,
|
||||
channel: vscode.OutputChannel): void {
|
||||
DigitalTwinUtility.extensionInstance =
|
||||
new ApiProvider(modelRepositoryManager);
|
||||
DigitalTwinUtility.channel = channel;
|
||||
|
@ -48,14 +48,14 @@ export class DigitalTwinUtility {
|
|||
}
|
||||
if (!result) {
|
||||
throw new CancelOperationError(
|
||||
`Selected device capability model file cancelled.`);
|
||||
`Selected device capability model file cancelled.`);
|
||||
}
|
||||
|
||||
utils.channelShowAndAppendLine(
|
||||
DigitalTwinUtility.channel,
|
||||
`${
|
||||
DigitalTwinConstants
|
||||
.dtPrefix} Selected device capability model file: ${result}`);
|
||||
DigitalTwinUtility.channel,
|
||||
`${
|
||||
DigitalTwinConstants
|
||||
.dtPrefix} Selected device capability model file: ${result}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -66,17 +66,17 @@ export class DigitalTwinUtility {
|
|||
* @param capabilityModelFile capability model file path
|
||||
*/
|
||||
static async downloadDependentInterface(
|
||||
folder: string, capabilityModelFile: string): Promise<boolean> {
|
||||
folder: string, capabilityModelFile: string): Promise<boolean> {
|
||||
if (!DigitalTwinUtility.extensionInstance) {
|
||||
throw new Error(DigitalTwinUtility.EXTENSION_NOT_INIT);
|
||||
}
|
||||
try {
|
||||
await DigitalTwinUtility.extensionInstance.downloadDependentInterface(
|
||||
folder, capabilityModelFile);
|
||||
folder, capabilityModelFile);
|
||||
} catch (error) {
|
||||
utils.channelShowAndAppendLine(
|
||||
DigitalTwinUtility.channel,
|
||||
`${DigitalTwinConstants.dtPrefix} ${error.message}`);
|
||||
DigitalTwinUtility.channel,
|
||||
`${DigitalTwinConstants.dtPrefix} ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Configuration {
|
|||
* @param name property name
|
||||
* @param value property value
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static async setGlobalProperty(name: string, value: any): Promise<void> {
|
||||
await Configuration.instance.update(name, value, true);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ export class Configuration {
|
|||
* @param name property name
|
||||
* @param value property value
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static async setWorkspaceProperty(name: string, value: any): Promise<void> {
|
||||
await Configuration.instance.update(name, value, false);
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ export class Utility {
|
|||
* @param modelId model id
|
||||
* @param content model content
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static async createModelFile(folder: string, modelId: string, content: any): Promise<void> {
|
||||
const type: ModelType = DeviceModelManager.convertToModelType(content[DigitalTwinConstants.TYPE]);
|
||||
if (!type) {
|
||||
|
@ -105,6 +105,7 @@ export class Utility {
|
|||
const modelName: string = Utility.replaceAll(modelId, replacement);
|
||||
let candidate: string = DeviceModelManager.generateModelFileName(modelName, type);
|
||||
let counter = 0;
|
||||
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
|
||||
while (true) {
|
||||
if (!(await fs.pathExists(path.join(folder, candidate)))) {
|
||||
break;
|
||||
|
@ -141,7 +142,7 @@ export class Utility {
|
|||
* get json content from file
|
||||
* @param filePath file path
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static async getJsonContent(filePath: string): Promise<any> {
|
||||
return fs.readJson(filePath, { encoding: Constants.UTF8 });
|
||||
}
|
||||
|
|
|
@ -106,13 +106,13 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
scanner.setPosition(offset);
|
||||
const token: parser.SyntaxKind = scanner.scan();
|
||||
switch (token) {
|
||||
case parser.SyntaxKind.CommaToken:
|
||||
case parser.SyntaxKind.CloseBraceToken:
|
||||
case parser.SyntaxKind.CloseBracketToken:
|
||||
case parser.SyntaxKind.EOF:
|
||||
return Constants.EMPTY_STRING;
|
||||
default:
|
||||
return Constants.DEFAULT_SEPARATOR;
|
||||
case parser.SyntaxKind.CommaToken:
|
||||
case parser.SyntaxKind.CloseBraceToken:
|
||||
case parser.SyntaxKind.CloseBracketToken:
|
||||
case parser.SyntaxKind.EOF:
|
||||
return Constants.EMPTY_STRING;
|
||||
default:
|
||||
return Constants.DEFAULT_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,16 +345,16 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
value = "{$1}";
|
||||
} else if (!classNode.label) {
|
||||
switch (classNode.id) {
|
||||
case ValueSchema.String:
|
||||
value = '"$1"';
|
||||
break;
|
||||
case ValueSchema.Int:
|
||||
value = "${1:0}";
|
||||
break;
|
||||
case ValueSchema.Boolean:
|
||||
value = "${1:false}";
|
||||
break;
|
||||
default:
|
||||
case ValueSchema.String:
|
||||
value = '"$1"';
|
||||
break;
|
||||
case ValueSchema.Int:
|
||||
value = "${1:0}";
|
||||
break;
|
||||
case ValueSchema.Boolean:
|
||||
value = "${1:false}";
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -453,11 +453,8 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
* @param token cancellation token
|
||||
* @param context completion context
|
||||
*/
|
||||
provideCompletionItems(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
token: vscode.CancellationToken,
|
||||
context: vscode.CompletionContext,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken, _context: vscode.CompletionContext,
|
||||
): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
|
||||
const text: string = DigitalTwinCompletionItemProvider.getTextForParse(document, position);
|
||||
const jsonNode: parser.Node | undefined = IntelliSenseUtility.parseDigitalTwinModel(text);
|
||||
|
|
|
@ -102,22 +102,22 @@ export class DigitalTwinDiagnosticProvider {
|
|||
private static validateNode(jsonNode: parser.Node, digitalTwinNode: PropertyNode, problems: Problem[]): void {
|
||||
const nodeType: parser.NodeType = jsonNode.type;
|
||||
switch (nodeType) {
|
||||
case JsonNodeType.Object:
|
||||
DigitalTwinDiagnosticProvider.validateObjectNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
case JsonNodeType.Array:
|
||||
DigitalTwinDiagnosticProvider.validateArrayNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
case JsonNodeType.String:
|
||||
DigitalTwinDiagnosticProvider.validateStringNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
case JsonNodeType.Number:
|
||||
DigitalTwinDiagnosticProvider.validateNumberNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
case JsonNodeType.Boolean:
|
||||
DigitalTwinDiagnosticProvider.validateBooleanNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
default:
|
||||
case JsonNodeType.Object:
|
||||
DigitalTwinDiagnosticProvider.validateObjectNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
case JsonNodeType.Array:
|
||||
DigitalTwinDiagnosticProvider.validateArrayNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
case JsonNodeType.String:
|
||||
DigitalTwinDiagnosticProvider.validateStringNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
case JsonNodeType.Number:
|
||||
DigitalTwinDiagnosticProvider.validateNumberNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
case JsonNodeType.Boolean:
|
||||
DigitalTwinDiagnosticProvider.validateBooleanNode(jsonNode, digitalTwinNode, problems);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,38 +259,38 @@ export class DigitalTwinDiagnosticProvider {
|
|||
// duplicate property name is handled by json validator
|
||||
exist.add(propertyName);
|
||||
switch (propertyName) {
|
||||
case DigitalTwinConstants.ID:
|
||||
// @id is available for each class
|
||||
propertyNode = IntelliSenseUtility.getPropertyNode(propertyName);
|
||||
if (propertyNode) {
|
||||
DigitalTwinDiagnosticProvider.validateNode(propertyPair.value, propertyNode, problems);
|
||||
}
|
||||
break;
|
||||
case DigitalTwinConstants.CONTEXT:
|
||||
// @context is available when it is required
|
||||
if (
|
||||
classNode.constraint &&
|
||||
case DigitalTwinConstants.ID:
|
||||
// @id is available for each class
|
||||
propertyNode = IntelliSenseUtility.getPropertyNode(propertyName);
|
||||
if (propertyNode) {
|
||||
DigitalTwinDiagnosticProvider.validateNode(propertyPair.value, propertyNode, problems);
|
||||
}
|
||||
break;
|
||||
case DigitalTwinConstants.CONTEXT:
|
||||
// @context is available when it is required
|
||||
if (
|
||||
classNode.constraint &&
|
||||
classNode.constraint.required &&
|
||||
classNode.constraint.required.includes(propertyName)
|
||||
) {
|
||||
if (!IntelliSenseUtility.isDigitalTwinContext(propertyPair.value)) {
|
||||
DigitalTwinDiagnosticProvider.addProblem(propertyPair.value, problems, DiagnosticMessage.InvalidContext);
|
||||
}
|
||||
} else {
|
||||
DigitalTwinDiagnosticProvider.addProblemOfUnexpectedProperty(propertyPair.name, problems);
|
||||
}
|
||||
break;
|
||||
case DigitalTwinConstants.TYPE:
|
||||
// skip since @type is already validated
|
||||
break;
|
||||
default:
|
||||
// validate expected property
|
||||
propertyNode = expectedProperties.get(propertyName);
|
||||
if (!propertyNode) {
|
||||
DigitalTwinDiagnosticProvider.addProblemOfUnexpectedProperty(propertyPair.name, problems);
|
||||
} else {
|
||||
DigitalTwinDiagnosticProvider.validateNode(propertyPair.value, propertyNode, problems);
|
||||
) {
|
||||
if (!IntelliSenseUtility.isDigitalTwinContext(propertyPair.value)) {
|
||||
DigitalTwinDiagnosticProvider.addProblem(propertyPair.value, problems, DiagnosticMessage.InvalidContext);
|
||||
}
|
||||
} else {
|
||||
DigitalTwinDiagnosticProvider.addProblemOfUnexpectedProperty(propertyPair.name, problems);
|
||||
}
|
||||
break;
|
||||
case DigitalTwinConstants.TYPE:
|
||||
// skip since @type is already validated
|
||||
break;
|
||||
default:
|
||||
// validate expected property
|
||||
propertyNode = expectedProperties.get(propertyName);
|
||||
if (!propertyNode) {
|
||||
DigitalTwinDiagnosticProvider.addProblemOfUnexpectedProperty(propertyPair.name, problems);
|
||||
} else {
|
||||
DigitalTwinDiagnosticProvider.validateNode(propertyPair.value, propertyNode, problems);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ export class DigitalTwinGraph {
|
|||
* check if json object is a valid constraint node
|
||||
* @param object object data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private static isConstraintNode(object: any): object is ConstraintNode {
|
||||
return (
|
||||
object.minItems || object.maxItems || object.minLength || object.maxLength || object.pattern || object.required
|
||||
|
@ -164,7 +164,7 @@ export class DigitalTwinGraph {
|
|||
* check if it is a valid edge
|
||||
* @param edge edge data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private static isValidEdge(edge: any): boolean {
|
||||
return edge.SourceNode && edge.TargetNode && edge.Label;
|
||||
}
|
||||
|
@ -173,20 +173,20 @@ export class DigitalTwinGraph {
|
|||
* resolve container type
|
||||
* @param object object data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private static resolveContainerType(object: any): ContainerType {
|
||||
const container = object[DigitalTwinConstants.CONTAINER];
|
||||
if (!container || typeof container !== "string") {
|
||||
return ContainerType.None;
|
||||
}
|
||||
switch (container) {
|
||||
case DigitalTwinConstants.LIST:
|
||||
case DigitalTwinConstants.SET:
|
||||
return ContainerType.Array;
|
||||
case DigitalTwinConstants.LANGUAGE:
|
||||
return ContainerType.Language;
|
||||
default:
|
||||
return ContainerType.None;
|
||||
case DigitalTwinConstants.LIST:
|
||||
case DigitalTwinConstants.SET:
|
||||
return ContainerType.Array;
|
||||
case DigitalTwinConstants.LANGUAGE:
|
||||
return ContainerType.Language;
|
||||
default:
|
||||
return ContainerType.None;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ export class DigitalTwinGraph {
|
|||
* @param context extension context
|
||||
* @param fileName file name
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private static async resolveDefinition(context: vscode.ExtensionContext, fileName: string): Promise<any> {
|
||||
const filePath: string = context.asAbsolutePath(
|
||||
path.join(Constants.RESOURCE_FOLDER, Constants.DEFINITION_FOLDER, fileName),
|
||||
|
@ -285,7 +285,7 @@ export class DigitalTwinGraph {
|
|||
* build context nodes by id and reversed index
|
||||
* @param contextJson json object of context definition
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private buildContext(contextJson: any): void {
|
||||
let id: string;
|
||||
const context = contextJson[DigitalTwinConstants.CONTEXT];
|
||||
|
@ -311,7 +311,7 @@ export class DigitalTwinGraph {
|
|||
* build constraint nodes by name
|
||||
* @param constraintJson json object of constraint definition
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private buildConstraint(constraintJson: any): void {
|
||||
for (const key in constraintJson) {
|
||||
if (DigitalTwinGraph.isConstraintNode(constraintJson[key])) {
|
||||
|
@ -324,7 +324,7 @@ export class DigitalTwinGraph {
|
|||
* build DigitalTwin graph on definitions of context, constraint and graph
|
||||
* @param graphJson json object of graph definition
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private buildGraph(graphJson: any): void {
|
||||
for (const edge of graphJson.Edges) {
|
||||
if (DigitalTwinGraph.isValidEdge(edge)) {
|
||||
|
@ -340,28 +340,28 @@ export class DigitalTwinGraph {
|
|||
* handle data of edge
|
||||
* @param edge edge data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private handleEdge(edge: any): void {
|
||||
switch (edge.Label) {
|
||||
case EdgeType.Type:
|
||||
this.handleEdgeOfType(edge);
|
||||
break;
|
||||
case EdgeType.Label:
|
||||
this.handleEdgeOfLabel(edge);
|
||||
break;
|
||||
case EdgeType.Domain:
|
||||
this.handleEdgeOfDomain(edge);
|
||||
break;
|
||||
case EdgeType.Range:
|
||||
this.handleEdgeOfRange(edge);
|
||||
break;
|
||||
case EdgeType.SubClassOf:
|
||||
this.handleEdgeOfSubClassOf(edge);
|
||||
break;
|
||||
case EdgeType.Comment:
|
||||
this.handleEdgeOfComment(edge);
|
||||
break;
|
||||
default:
|
||||
case EdgeType.Type:
|
||||
this.handleEdgeOfType(edge);
|
||||
break;
|
||||
case EdgeType.Label:
|
||||
this.handleEdgeOfLabel(edge);
|
||||
break;
|
||||
case EdgeType.Domain:
|
||||
this.handleEdgeOfDomain(edge);
|
||||
break;
|
||||
case EdgeType.Range:
|
||||
this.handleEdgeOfRange(edge);
|
||||
break;
|
||||
case EdgeType.SubClassOf:
|
||||
this.handleEdgeOfSubClassOf(edge);
|
||||
break;
|
||||
case EdgeType.Comment:
|
||||
this.handleEdgeOfComment(edge);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,26 +371,27 @@ export class DigitalTwinGraph {
|
|||
* 2. add enum value to enum node
|
||||
* @param edge edge data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private handleEdgeOfType(edge: any): void {
|
||||
const id: string = edge.SourceNode.Id as string;
|
||||
const type: string = edge.TargetNode.Id as string;
|
||||
switch (type) {
|
||||
case NodeType.Class:
|
||||
this.ensureClassNode(id);
|
||||
break;
|
||||
case NodeType.Property:
|
||||
this.ensurePropertyNode(id);
|
||||
break;
|
||||
default:
|
||||
// mark target class as enum node
|
||||
const contextNode: ContextNode | undefined = this.contextNodes.get(id);
|
||||
const enumValue: string = contextNode ? contextNode.name : id;
|
||||
const enumNode: ClassNode = this.ensureClassNode(type);
|
||||
if (!enumNode.enums) {
|
||||
enumNode.enums = [];
|
||||
}
|
||||
enumNode.enums.push(enumValue);
|
||||
case NodeType.Class:
|
||||
this.ensureClassNode(id);
|
||||
break;
|
||||
case NodeType.Property:
|
||||
this.ensurePropertyNode(id);
|
||||
break;
|
||||
default:{
|
||||
// mark target class as enum node
|
||||
const contextNode: ContextNode | undefined = this.contextNodes.get(id);
|
||||
const enumValue: string = contextNode ? contextNode.name : id;
|
||||
const enumNode: ClassNode = this.ensureClassNode(type);
|
||||
if (!enumNode.enums) {
|
||||
enumNode.enums = [];
|
||||
}
|
||||
enumNode.enums.push(enumValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,7 +401,7 @@ export class DigitalTwinGraph {
|
|||
* 2. set label and constraint if not defined
|
||||
* @param edge edge data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private handleEdgeOfLabel(edge: any): void {
|
||||
const id: string = edge.SourceNode.Id as string;
|
||||
const label: string = edge.TargetNode.Value as string;
|
||||
|
@ -424,7 +425,7 @@ export class DigitalTwinGraph {
|
|||
* 1. add property to class node
|
||||
* @param edge edge data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private handleEdgeOfDomain(edge: any): void {
|
||||
const id: string = edge.SourceNode.Id as string;
|
||||
const classId: string = edge.TargetNode.Id as string;
|
||||
|
@ -441,7 +442,7 @@ export class DigitalTwinGraph {
|
|||
* 1. add range to property node
|
||||
* @param edge edge data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private handleEdgeOfRange(edge: any): void {
|
||||
const id: string = edge.SourceNode.Id as string;
|
||||
const classId: string = edge.TargetNode.Id as string;
|
||||
|
@ -458,7 +459,7 @@ export class DigitalTwinGraph {
|
|||
* 1. add children to base class node
|
||||
* @param edge edge data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private handleEdgeOfSubClassOf(edge: any): void {
|
||||
const id: string = edge.SourceNode.Id as string;
|
||||
const baseId: string = edge.TargetNode.Id as string;
|
||||
|
@ -475,7 +476,7 @@ export class DigitalTwinGraph {
|
|||
* 1. set comment of property node
|
||||
* @param edge edge data
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private handleEdgeOfComment(edge: any): void {
|
||||
const id: string = edge.SourceNode.Id as string;
|
||||
const comment: string = edge.TargetNode.Value as string;
|
||||
|
|
|
@ -21,15 +21,16 @@ export class DigitalTwinHoverProvider implements vscode.HoverProvider {
|
|||
return Constants.EMPTY_STRING;
|
||||
}
|
||||
switch (propertyName) {
|
||||
case DigitalTwinConstants.ID:
|
||||
return `An identifier for ${Constants.CHANNEL_NAME} Capability Model or interface`;
|
||||
case DigitalTwinConstants.TYPE:
|
||||
return `The type of ${Constants.CHANNEL_NAME} meta model object`;
|
||||
case DigitalTwinConstants.CONTEXT:
|
||||
return `The context for ${Constants.CHANNEL_NAME} Capability Model or interface`;
|
||||
default:
|
||||
const propertyNode: PropertyNode | undefined = IntelliSenseUtility.getPropertyNode(propertyName);
|
||||
return propertyNode && propertyNode.comment ? propertyNode.comment : Constants.EMPTY_STRING;
|
||||
case DigitalTwinConstants.ID:
|
||||
return `An identifier for ${Constants.CHANNEL_NAME} Capability Model or interface`;
|
||||
case DigitalTwinConstants.TYPE:
|
||||
return `The type of ${Constants.CHANNEL_NAME} meta model object`;
|
||||
case DigitalTwinConstants.CONTEXT:
|
||||
return `The context for ${Constants.CHANNEL_NAME} Capability Model or interface`;
|
||||
default: {
|
||||
const propertyNode: PropertyNode | undefined = IntelliSenseUtility.getPropertyNode(propertyName);
|
||||
return propertyNode && propertyNode.comment ? propertyNode.comment : Constants.EMPTY_STRING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +43,8 @@ export class DigitalTwinHoverProvider implements vscode.HoverProvider {
|
|||
provideHover(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
token: vscode.CancellationToken,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_token: vscode.CancellationToken,
|
||||
): vscode.ProviderResult<vscode.Hover> {
|
||||
const jsonNode: parser.Node | undefined = IntelliSenseUtility.parseDigitalTwinModel(document.getText());
|
||||
if (!jsonNode) {
|
||||
|
|
|
@ -90,7 +90,7 @@ export class ModelRepositoryClient {
|
|||
* @param modelId model id
|
||||
* @param content content to update
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static async updateModel(repoInfo: RepositoryInfo, modelId: string, content: any): Promise<string> {
|
||||
const options: request.OptionsWithUri = ModelRepositoryClient.createOptions(HttpMethod.Put, repoInfo, modelId);
|
||||
options.body = content;
|
||||
|
@ -132,12 +132,12 @@ export class ModelRepositoryClient {
|
|||
*/
|
||||
private static convertToMetaModelType(type: ModelType): MetaModelType {
|
||||
switch (type) {
|
||||
case ModelType.Interface:
|
||||
return MetaModelType.Interface;
|
||||
case ModelType.CapabilityModel:
|
||||
return MetaModelType.CapabilityModel;
|
||||
default:
|
||||
return MetaModelType.None;
|
||||
case ModelType.Interface:
|
||||
return MetaModelType.Interface;
|
||||
case ModelType.CapabilityModel:
|
||||
return MetaModelType.CapabilityModel;
|
||||
default:
|
||||
return MetaModelType.None;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ export class ModelRepositoryClient {
|
|||
const uri = modelId
|
||||
? `${repoInfo.hostname}/models/${encodeURIComponent(modelId)}`
|
||||
: `${repoInfo.hostname}/models/search`;
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const qs: any = { "api-version": repoInfo.apiVersion };
|
||||
if (repoInfo.repositoryId) {
|
||||
qs.repositoryId = repoInfo.repositoryId;
|
||||
|
|
|
@ -325,7 +325,7 @@ export class ModelRepositoryManager {
|
|||
const repoInfos: RepositoryInfo[] = await ModelRepositoryManager.getAvailableRepositoryInfo();
|
||||
const fileInfos: ModelFileInfo[] = await UI.findModelFiles(ModelType.Interface);
|
||||
const exist = new Set<string>(fileInfos.map(f => f.id));
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let schema: any;
|
||||
let found: boolean;
|
||||
let message: string;
|
||||
|
@ -512,13 +512,13 @@ export class ModelRepositoryManager {
|
|||
succeedCount += value.length;
|
||||
hashId = value.map(id => Utility.hash(id)).join(Constants.DEFAULT_SEPARATOR);
|
||||
switch (key) {
|
||||
case ModelType.Interface:
|
||||
telemetryContext.properties.interfaceId = hashId;
|
||||
break;
|
||||
case ModelType.CapabilityModel:
|
||||
telemetryContext.properties.capabilityModelId = hashId;
|
||||
break;
|
||||
default:
|
||||
case ModelType.Interface:
|
||||
telemetryContext.properties.interfaceId = hashId;
|
||||
break;
|
||||
case ModelType.CapabilityModel:
|
||||
telemetryContext.properties.capabilityModelId = hashId;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
telemetryContext.measurements.totalCount = totalCount;
|
||||
|
|
|
@ -57,16 +57,16 @@ export class UI {
|
|||
*/
|
||||
static showNotification(type: MessageType, message: string): void {
|
||||
switch (type) {
|
||||
case MessageType.Info:
|
||||
vscode.window.showInformationMessage(message);
|
||||
break;
|
||||
case MessageType.Warn:
|
||||
vscode.window.showWarningMessage(message);
|
||||
break;
|
||||
case MessageType.Error:
|
||||
vscode.window.showErrorMessage(message);
|
||||
break;
|
||||
default:
|
||||
case MessageType.Info:
|
||||
vscode.window.showInformationMessage(message);
|
||||
break;
|
||||
case MessageType.Warn:
|
||||
vscode.window.showWarningMessage(message);
|
||||
break;
|
||||
case MessageType.Error:
|
||||
vscode.window.showErrorMessage(message);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ export class UI {
|
|||
*/
|
||||
static async inputModelName(label: string, type: ModelType, folder: string): Promise<string> {
|
||||
const placeHolder = `${type} name`;
|
||||
const validateInput = async (name: string) => {
|
||||
const validateInput = async (name: string): Promise<string|undefined> => {
|
||||
return await Utility.validateModelName(name, type, folder);
|
||||
};
|
||||
return await UI.showInputBox(label, placeHolder, validateInput);
|
||||
|
@ -181,7 +181,7 @@ export class UI {
|
|||
* @param label label
|
||||
*/
|
||||
static async inputConnectionString(label: string): Promise<string> {
|
||||
const validateInput = (name: string) => {
|
||||
const validateInput = (name: string): string|undefined => {
|
||||
return Utility.validateNotEmpty(name, "Connection string");
|
||||
};
|
||||
return await UI.showInputBox(label, UIConstants.REPOSITORY_CONNECTION_STRING_TEMPLATE, validateInput);
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as fs from 'fs-plus';
|
|||
import * as path from 'path';
|
||||
import * as sdk from 'vscode-iot-device-cube-sdk';
|
||||
import * as crypto from 'crypto';
|
||||
import {ScaffoldType} from './constants';
|
||||
import { ScaffoldType } from './constants';
|
||||
import extractzip = require('extract-zip');
|
||||
|
||||
export class FileUtility {
|
||||
|
@ -57,7 +57,7 @@ export class FileUtility {
|
|||
if (type === ScaffoldType.Local) {
|
||||
return await sdk.FileSystem.mkDir(dirPath);
|
||||
} else {
|
||||
return new Promise(async (resolve: (value?: void) => void, reject) => {
|
||||
return new Promise((resolve: (value?: void) => void, reject) => {
|
||||
fs.mkdir(dirPath, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
|
@ -88,13 +88,13 @@ export class FileUtility {
|
|||
|
||||
// Make sure filepath's parent directory exists
|
||||
static async writeFile(
|
||||
type: ScaffoldType, filePath: string,
|
||||
data: string|Buffer): Promise<void> {
|
||||
type: ScaffoldType, filePath: string,
|
||||
data: string|Buffer): Promise<void> {
|
||||
if (type === ScaffoldType.Local) {
|
||||
return await sdk.FileSystem.writeFile(filePath, data);
|
||||
} else {
|
||||
return new Promise(async (resolve: (value?: void) => void, reject) => {
|
||||
await fs.writeFile(filePath, data, (err) => {
|
||||
return new Promise((resolve: (value?: void) => void, reject) => {
|
||||
fs.writeFile(filePath, data, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
|
@ -113,8 +113,8 @@ export class FileUtility {
|
|||
* @param data Json object
|
||||
*/
|
||||
static async writeJsonFile(
|
||||
// tslint:disable-next-line: no-any
|
||||
type: ScaffoldType, fileDestPath: string, data: any): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: ScaffoldType, fileDestPath: string, data: any): Promise<void> {
|
||||
const indentationSpace = 4;
|
||||
const jsonString = JSON.stringify(data, null, indentationSpace);
|
||||
|
||||
|
@ -126,29 +126,29 @@ export class FileUtility {
|
|||
}
|
||||
|
||||
static async readFile(
|
||||
type: ScaffoldType, filePath: string,
|
||||
encoding?: string): Promise<string|Buffer> {
|
||||
type: ScaffoldType, filePath: string,
|
||||
encoding?: string): Promise<string|Buffer> {
|
||||
if (type === ScaffoldType.Local) {
|
||||
return await sdk.FileSystem.readFile(filePath, encoding);
|
||||
} else {
|
||||
return new Promise(
|
||||
async (resolve: (data: string|Buffer) => void, reject) => {
|
||||
await fs.readFile(filePath, encoding, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(data);
|
||||
(resolve: (data: string|Buffer) => void, reject) => {
|
||||
fs.readFile(filePath, encoding, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
});
|
||||
}
|
||||
resolve(data);
|
||||
return;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async extractZipFile(sourceZip: string, targetFoder: string):
|
||||
Promise<boolean> {
|
||||
return new Promise((resolve: (value: boolean) => void, reject) => {
|
||||
extractzip(sourceZip, {dir: targetFoder}, err => {
|
||||
extractzip(sourceZip, { dir: targetFoder }, err => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
} else {
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {CancelOperationError} from './CancelOperationError';
|
||||
import {ConfigHandler} from './configHandler';
|
||||
import {ConfigKey, OSPlatform} from './constants';
|
||||
import {PickWithData} from './Models/Interfaces/UI';
|
||||
import {getHomeDir, getPlatform} from './utils';
|
||||
import { CancelOperationError } from './CancelOperationError';
|
||||
import { ConfigHandler } from './configHandler';
|
||||
import { ConfigKey, OSPlatform } from './constants';
|
||||
import { PickWithData } from './Models/Interfaces/UI';
|
||||
import { getHomeDir, getPlatform } from './utils';
|
||||
|
||||
export class IoTWorkbenchSettings {
|
||||
private workbenchPath = '';
|
||||
|
@ -23,8 +23,8 @@ export class IoTWorkbenchSettings {
|
|||
ConfigHandler.get<string>(ConfigKey.workbench) ||
|
||||
(await this.getDefaultWorkbenchPath());
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.workbench, IoTWorkbenchSettings.instance.workbenchPath,
|
||||
vscode.ConfigurationTarget.Global);
|
||||
ConfigKey.workbench, IoTWorkbenchSettings.instance.workbenchPath,
|
||||
vscode.ConfigurationTarget.Global);
|
||||
}
|
||||
|
||||
return IoTWorkbenchSettings.instance;
|
||||
|
@ -64,18 +64,18 @@ export class IoTWorkbenchSettings {
|
|||
|
||||
if (userWorkbenchPath) {
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.workbench, userWorkbenchPath,
|
||||
vscode.ConfigurationTarget.Global);
|
||||
ConfigKey.workbench, userWorkbenchPath,
|
||||
vscode.ConfigurationTarget.Global);
|
||||
await vscode.window.showInformationMessage(
|
||||
'Change workbench successfully.');
|
||||
'Change workbench successfully.');
|
||||
}
|
||||
}
|
||||
|
||||
private async selectWorkbenchPath(): Promise<PickWithData<string>> {
|
||||
const userWorkbenchPath = this.getWorkbenchPath();
|
||||
const workbenchPicks: Array<PickWithData<string>> = [
|
||||
{label: userWorkbenchPath, description: '', data: userWorkbenchPath},
|
||||
{label: '$(file-directory) Browse...', description: '', data: '$'}
|
||||
{ label: userWorkbenchPath, description: '', data: userWorkbenchPath },
|
||||
{ label: '$(file-directory) Browse...', description: '', data: '$' }
|
||||
];
|
||||
|
||||
const selection = await vscode.window.showQuickPick(workbenchPicks, {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import * as crypto from 'crypto';
|
||||
import * as fs from 'fs-plus';
|
||||
import * as getmac from 'getmac';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import { Guid } from 'guid-typescript';
|
||||
import * as _ from 'lodash';
|
||||
import * as opn from 'opn';
|
||||
import * as os from 'os';
|
||||
|
@ -12,18 +12,20 @@ import * as path from 'path';
|
|||
import * as vscode from 'vscode';
|
||||
import * as WinReg from 'winreg';
|
||||
|
||||
import {BoardProvider} from '../boardProvider';
|
||||
import {ArduinoCommands} from '../common/Commands';
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {ConfigKey, FileNames, OSPlatform, ScaffoldType} from '../constants';
|
||||
import {DialogResponses} from '../DialogResponses';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {TelemetryContext} from '../telemetry';
|
||||
import {delay, getRegistryValues} from '../utils';
|
||||
import { BoardProvider } from '../boardProvider';
|
||||
import { ArduinoCommands } from '../common/Commands';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { ConfigKey, FileNames, OSPlatform, ScaffoldType } from '../constants';
|
||||
import { DialogResponses } from '../DialogResponses';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { TelemetryContext } from '../telemetry';
|
||||
import { delay, getRegistryValues } from '../utils';
|
||||
|
||||
import {ArduinoDeviceBase} from './ArduinoDeviceBase';
|
||||
import {DeviceType} from './Interfaces/Device';
|
||||
import {DeviceConfig, TemplateFileInfo} from './Interfaces/ProjectTemplate';
|
||||
import { ArduinoDeviceBase } from './ArduinoDeviceBase';
|
||||
import { DeviceType } from './Interfaces/Device';
|
||||
import { DeviceConfig, TemplateFileInfo } from './Interfaces/ProjectTemplate';
|
||||
import { Board } from './Interfaces/Board';
|
||||
import { reject } from 'bluebird';
|
||||
|
||||
const impor = require('impor')(__dirname);
|
||||
const forEach = impor('lodash.foreach') as typeof import('lodash.foreach');
|
||||
|
@ -53,7 +55,7 @@ enum ConfigDeviceOptions {
|
|||
}
|
||||
|
||||
export class AZ3166Device extends ArduinoDeviceBase {
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static get serialport(): any {
|
||||
if (!AZ3166Device._serialport) {
|
||||
AZ3166Device._serialport =
|
||||
|
@ -62,28 +64,28 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
return AZ3166Device._serialport;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private static _serialport: any;
|
||||
|
||||
private componentId: string;
|
||||
get id() {
|
||||
get id(): string {
|
||||
return this.componentId;
|
||||
}
|
||||
|
||||
private templateFiles: TemplateFileInfo[] = [];
|
||||
private static _boardId = 'devkit';
|
||||
|
||||
static get boardId() {
|
||||
static get boardId(): string {
|
||||
return AZ3166Device._boardId;
|
||||
}
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, devicePath: string,
|
||||
templateFiles?: TemplateFileInfo[]) {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, devicePath: string,
|
||||
templateFiles?: TemplateFileInfo[]) {
|
||||
super(
|
||||
context, devicePath, channel, telemetryContext,
|
||||
DeviceType.MXChip_AZ3166);
|
||||
context, devicePath, channel, telemetryContext,
|
||||
DeviceType.MXChipAZ3166);
|
||||
this.channel = channel;
|
||||
this.componentId = Guid.create().toString();
|
||||
if (templateFiles) {
|
||||
|
@ -93,13 +95,13 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
|
||||
name = 'AZ3166';
|
||||
|
||||
get board() {
|
||||
get board(): Board|undefined {
|
||||
const boardProvider = new BoardProvider(this.boardFolderPath);
|
||||
const az3166 = boardProvider.find({id: AZ3166Device._boardId});
|
||||
const az3166 = boardProvider.find({ id: AZ3166Device._boardId });
|
||||
return az3166;
|
||||
}
|
||||
|
||||
get version() {
|
||||
get version(): string {
|
||||
const packageRootPath = this.getArduinoPackagePath();
|
||||
let version = '0.0.1';
|
||||
|
||||
|
@ -133,8 +135,8 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
'The ST-LINK driver for DevKit is not installed. Install now?';
|
||||
const result: vscode.MessageItem|undefined =
|
||||
await vscode.window.showWarningMessage(
|
||||
message, DialogResponses.yes, DialogResponses.skipForNow,
|
||||
DialogResponses.cancel);
|
||||
message, DialogResponses.yes, DialogResponses.skipForNow,
|
||||
DialogResponses.cancel);
|
||||
if (result === DialogResponses.yes) {
|
||||
// Open the download page
|
||||
const installUri =
|
||||
|
@ -165,7 +167,7 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
|
||||
if (configSelection.detail === 'Config CRC') {
|
||||
const retValue: boolean =
|
||||
await this.generateCrc(this.extensionContext, this.channel);
|
||||
await this.generateCrc(this.channel);
|
||||
return retValue;
|
||||
} else if (configSelection.detail === 'Config Connection String') {
|
||||
// Get IoT Hub device connection string from config
|
||||
|
@ -174,8 +176,8 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
const deviceConnectionStringSelection =
|
||||
this.getDeviceConnectionStringOptions(deviceConnectionString);
|
||||
const selection = await vscode.window.showQuickPick(
|
||||
deviceConnectionStringSelection,
|
||||
{ignoreFocusOut: true, placeHolder: 'Choose an option:'});
|
||||
deviceConnectionStringSelection,
|
||||
{ ignoreFocusOut: true, placeHolder: 'Choose an option:' });
|
||||
if (!selection) {
|
||||
return false;
|
||||
}
|
||||
|
@ -188,7 +190,7 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
'Need more information on how to get device connection string?';
|
||||
const result: vscode.MessageItem|undefined =
|
||||
await vscode.window.showWarningMessage(
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
if (result === DialogResponses.yes) {
|
||||
opn(constants.informationPageUrl);
|
||||
}
|
||||
|
@ -201,12 +203,12 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
|
||||
console.log(deviceConnectionString);
|
||||
const res = await this.setDeviceConfig(
|
||||
deviceConnectionString, ConfigDeviceOptions.ConnectionString);
|
||||
deviceConnectionString, ConfigDeviceOptions.ConnectionString);
|
||||
if (!res) {
|
||||
return false;
|
||||
} else {
|
||||
vscode.window.showInformationMessage(
|
||||
'Configure Device connection string completely.');
|
||||
'Configure Device connection string completely.');
|
||||
return true;
|
||||
}
|
||||
} else if (configSelection.detail === 'Config DPS') {
|
||||
|
@ -218,12 +220,12 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
|
||||
console.log(deviceConnectionString);
|
||||
const res = await this.setDeviceConfig(
|
||||
deviceConnectionString, ConfigDeviceOptions.DPS);
|
||||
deviceConnectionString, ConfigDeviceOptions.DPS);
|
||||
if (!res) {
|
||||
return false;
|
||||
} else {
|
||||
vscode.window.showInformationMessage(
|
||||
'Config DPS credentials completely.');
|
||||
'Config DPS credentials completely.');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -240,7 +242,7 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
return false;
|
||||
} else {
|
||||
vscode.window.showInformationMessage(
|
||||
'Configure Unique Device String (UDS) completed successfully.');
|
||||
'Configure Unique Device String (UDS) completed successfully.');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -251,23 +253,23 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
// Read options configuration JSON
|
||||
const devciceConfigFilePath: string =
|
||||
this.extensionContext.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.configDeviceOptionsFileName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.configDeviceOptionsFileName));
|
||||
|
||||
const configSelectionItemsFilePath = await FileUtility.readFile(
|
||||
ScaffoldType.Local, devciceConfigFilePath, 'utf8');
|
||||
ScaffoldType.Local, devciceConfigFilePath, 'utf8');
|
||||
const configSelectionItemsContent =
|
||||
JSON.parse(configSelectionItemsFilePath as string);
|
||||
|
||||
const configSelectionItems: vscode.QuickPickItem[] = [];
|
||||
configSelectionItemsContent.configSelectionItems.forEach(
|
||||
(element: DeviceConfig) => {
|
||||
configSelectionItems.push({
|
||||
label: element.label,
|
||||
description: element.description,
|
||||
detail: element.detail
|
||||
});
|
||||
(element: DeviceConfig) => {
|
||||
configSelectionItems.push({
|
||||
label: element.label,
|
||||
description: element.description,
|
||||
detail: element.detail
|
||||
});
|
||||
});
|
||||
|
||||
return configSelectionItems;
|
||||
}
|
||||
|
@ -390,8 +392,9 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
// Try to close serial monitor
|
||||
try {
|
||||
await vscode.commands.executeCommand(
|
||||
ArduinoCommands.CloseSerialMonitor, null, false);
|
||||
ArduinoCommands.CloseSerialMonitor, null, false);
|
||||
} catch (ignore) {
|
||||
// Ignore error if fail to close serial monitor
|
||||
}
|
||||
|
||||
// Set selected connection string to device
|
||||
|
@ -406,339 +409,346 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
async flushDeviceConfigUnixAndMac(configValue: string, option: number):
|
||||
Promise<boolean> {
|
||||
return new Promise(
|
||||
async (
|
||||
resolve: (value: boolean) => void,
|
||||
reject: (value: Error) => void) => {
|
||||
let comPort = '';
|
||||
let command = '';
|
||||
try {
|
||||
// Choose COM port that AZ3166 is connected
|
||||
comPort = await this.chooseCOM();
|
||||
console.log(`Opening ${comPort}.`);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
if (option === ConfigDeviceOptions.ConnectionString) {
|
||||
command = 'set_az_iothub';
|
||||
} else if (option === ConfigDeviceOptions.DPS) {
|
||||
command = 'set_az_iotdps';
|
||||
} else {
|
||||
command = 'set_dps_uds';
|
||||
}
|
||||
let errorRejected = false;
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
async (
|
||||
resolve: (value: boolean) => void,
|
||||
reject: (value: Error) => void) => {
|
||||
let comPort = '';
|
||||
let command = '';
|
||||
try {
|
||||
// Choose COM port that AZ3166 is connected
|
||||
comPort = await this.chooseCOM();
|
||||
console.log(`Opening ${comPort}.`);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
if (option === ConfigDeviceOptions.ConnectionString) {
|
||||
command = 'set_az_iothub';
|
||||
} else if (option === ConfigDeviceOptions.DPS) {
|
||||
command = 'set_az_iotdps';
|
||||
} else {
|
||||
command = 'set_dps_uds';
|
||||
}
|
||||
let errorRejected = false;
|
||||
|
||||
const az3166 = this.board;
|
||||
const az3166 = this.board;
|
||||
|
||||
if (!az3166) {
|
||||
return reject(
|
||||
new Error('IoT DevKit is not found in the board list.'));
|
||||
}
|
||||
if (!az3166) {
|
||||
return reject(
|
||||
new Error('IoT DevKit is not found in the board list.'));
|
||||
}
|
||||
|
||||
const port = new AZ3166Device.serialport(comPort, {
|
||||
baudRate: az3166.defaultBaudRate,
|
||||
dataBits: 8,
|
||||
stopBits: 1,
|
||||
xon: false,
|
||||
xoff: false,
|
||||
parity: 'none'
|
||||
});
|
||||
const port = new AZ3166Device.serialport(comPort, {
|
||||
baudRate: az3166.defaultBaudRate,
|
||||
dataBits: 8,
|
||||
stopBits: 1,
|
||||
xon: false,
|
||||
xoff: false,
|
||||
parity: 'none'
|
||||
});
|
||||
|
||||
const rejectIfError = (err: Error) => {
|
||||
if (errorRejected) return true;
|
||||
if (err) {
|
||||
errorRejected = true;
|
||||
reject(err);
|
||||
try {
|
||||
port.close();
|
||||
} catch (ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const executeSetAzIoTHub = async () => {
|
||||
const rejectIfError = (err: Error): boolean => {
|
||||
if (errorRejected) return true;
|
||||
if (err) {
|
||||
errorRejected = true;
|
||||
reject(err);
|
||||
try {
|
||||
const data = `${command} "${configValue}"\r\n`;
|
||||
|
||||
let restDataLength = data.length;
|
||||
while (restDataLength > 0) {
|
||||
const start = data.length - restDataLength;
|
||||
const length = Math.min(100, restDataLength);
|
||||
restDataLength -= length;
|
||||
const dataChunk = data.substr(start, length);
|
||||
await this.sendDataViaSerialPort(port, dataChunk);
|
||||
await delay(1000);
|
||||
}
|
||||
|
||||
port.close();
|
||||
} catch (ignore) {
|
||||
// ignore error if fail to close port.
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const executeSetAzIoTHub = async (): Promise<void> => {
|
||||
try {
|
||||
const data = `${command} "${configValue}"\r\n`;
|
||||
|
||||
let restDataLength = data.length;
|
||||
while (restDataLength > 0) {
|
||||
const start = data.length - restDataLength;
|
||||
const length = Math.min(100, restDataLength);
|
||||
restDataLength -= length;
|
||||
const dataChunk = data.substr(start, length);
|
||||
await this.sendDataViaSerialPort(port, dataChunk);
|
||||
await delay(1000);
|
||||
}
|
||||
|
||||
if (errorRejected) {
|
||||
return;
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
};
|
||||
port.close();
|
||||
} catch (ignore) {
|
||||
// Ignore error if fail to close port
|
||||
}
|
||||
|
||||
// Configure serial port callbacks
|
||||
port.on('open', async () => {
|
||||
// tslint:disable-next-line: no-any
|
||||
await vscode.window.showInformationMessage(
|
||||
'Please hold down button A and then push and release the reset button to enter configuration mode. After enter configuration mode, click OK.',
|
||||
'OK');
|
||||
executeSetAzIoTHub()
|
||||
.then(() => resolve(true))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
if (errorRejected) {
|
||||
return;
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
};
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
port.on('error', (error: any) => {
|
||||
if (errorRejected) return;
|
||||
console.log(error);
|
||||
rejectIfError(error);
|
||||
});
|
||||
// Configure serial port callbacks
|
||||
port.on('open', async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await vscode.window.showInformationMessage(
|
||||
'Please hold down button A and then push and release the reset button to enter configuration mode. After enter configuration mode, click OK.',
|
||||
'OK');
|
||||
executeSetAzIoTHub()
|
||||
.then(() => resolve(true))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
port.on('error', (error: any) => {
|
||||
if (errorRejected) return;
|
||||
console.log(error);
|
||||
rejectIfError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async flushDeviceConfig(configValue: string, option: number):
|
||||
Promise<boolean> {
|
||||
return new Promise(
|
||||
async (
|
||||
resolve: (value: boolean) => void,
|
||||
reject: (value: Error) => void) => {
|
||||
let comPort = '';
|
||||
let command = '';
|
||||
try {
|
||||
// Choose COM port that AZ3166 is connected
|
||||
comPort = await this.chooseCOM();
|
||||
console.log(`Opening ${comPort}.`);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
if (option === ConfigDeviceOptions.ConnectionString) {
|
||||
command = 'set_az_iothub';
|
||||
} else if (option === ConfigDeviceOptions.DPS) {
|
||||
command = 'set_az_iotdps';
|
||||
} else {
|
||||
command = 'set_dps_uds';
|
||||
}
|
||||
let configMode = false;
|
||||
let errorRejected = false;
|
||||
let commandExecuted = false;
|
||||
let gotData = false;
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
async (
|
||||
resolve: (value: boolean) => void,
|
||||
reject: (value: Error) => void) => {
|
||||
let comPort = '';
|
||||
let command = '';
|
||||
try {
|
||||
// Choose COM port that AZ3166 is connected
|
||||
comPort = await this.chooseCOM();
|
||||
console.log(`Opening ${comPort}.`);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
if (option === ConfigDeviceOptions.ConnectionString) {
|
||||
command = 'set_az_iothub';
|
||||
} else if (option === ConfigDeviceOptions.DPS) {
|
||||
command = 'set_az_iotdps';
|
||||
} else {
|
||||
command = 'set_dps_uds';
|
||||
}
|
||||
let configMode = false;
|
||||
let errorRejected = false;
|
||||
let commandExecuted = false;
|
||||
let gotData = false;
|
||||
|
||||
const az3166 = this.board;
|
||||
const az3166 = this.board;
|
||||
|
||||
if (!az3166) {
|
||||
return reject(
|
||||
new Error('IoT DevKit is not found in the board list.'));
|
||||
}
|
||||
if (!az3166) {
|
||||
return reject(
|
||||
new Error('IoT DevKit is not found in the board list.'));
|
||||
}
|
||||
|
||||
const port = new AZ3166Device.serialport(comPort, {
|
||||
baudRate: az3166.defaultBaudRate,
|
||||
dataBits: 8,
|
||||
stopBits: 1,
|
||||
xon: false,
|
||||
xoff: false,
|
||||
parity: 'none'
|
||||
});
|
||||
const port = new AZ3166Device.serialport(comPort, {
|
||||
baudRate: az3166.defaultBaudRate,
|
||||
dataBits: 8,
|
||||
stopBits: 1,
|
||||
xon: false,
|
||||
xoff: false,
|
||||
parity: 'none'
|
||||
});
|
||||
|
||||
const rejectIfError = (err: Error) => {
|
||||
if (errorRejected) return true;
|
||||
if (err) {
|
||||
errorRejected = true;
|
||||
reject(err);
|
||||
try {
|
||||
port.close();
|
||||
} catch (ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const executeSetAzIoTHub = async () => {
|
||||
const rejectIfError = (err: Error): boolean => {
|
||||
if (errorRejected) return true;
|
||||
if (err) {
|
||||
errorRejected = true;
|
||||
reject(err);
|
||||
try {
|
||||
const data = `${command} "${configValue}"\r\n`;
|
||||
const maxDataLength = 256;
|
||||
await this.sendDataViaSerialPort(
|
||||
port, data.slice(0, maxDataLength));
|
||||
if (data.length > maxDataLength) {
|
||||
await delay(1000);
|
||||
await this.sendDataViaSerialPort(
|
||||
port, data.slice(maxDataLength));
|
||||
}
|
||||
|
||||
await delay(1000);
|
||||
port.close();
|
||||
} catch (ignore) {
|
||||
// Ignore error if fail to close port
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const executeSetAzIoTHub = async (): Promise<void> => {
|
||||
try {
|
||||
const data = `${command} "${configValue}"\r\n`;
|
||||
const maxDataLength = 256;
|
||||
await this.sendDataViaSerialPort(
|
||||
port, data.slice(0, maxDataLength));
|
||||
if (data.length > maxDataLength) {
|
||||
await delay(1000);
|
||||
await this.sendDataViaSerialPort(
|
||||
port, data.slice(maxDataLength));
|
||||
}
|
||||
|
||||
if (errorRejected) {
|
||||
return;
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
};
|
||||
await delay(1000);
|
||||
port.close();
|
||||
} catch (ignore) {
|
||||
// Ignore error if fail to close port
|
||||
}
|
||||
|
||||
// Configure serial port callbacks
|
||||
port.on('open', () => {
|
||||
port.write(
|
||||
'\r\nhelp\r\n',
|
||||
// tslint:disable-next-line: no-any
|
||||
(error: any) => {
|
||||
if (rejectIfError(error)) return;
|
||||
});
|
||||
});
|
||||
if (errorRejected) {
|
||||
return;
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
};
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
port.on('data', (data: any) => {
|
||||
gotData = true;
|
||||
const output = data.toString().trim();
|
||||
|
||||
if (commandExecuted) return;
|
||||
if (output.includes('set_')) {
|
||||
commandExecuted = true;
|
||||
configMode = true;
|
||||
executeSetAzIoTHub()
|
||||
.then(() => resolve(true))
|
||||
.catch((error) => reject(error));
|
||||
} else {
|
||||
configMode = false;
|
||||
}
|
||||
|
||||
if (configMode) {
|
||||
forEach(output.split('\n'), line => {
|
||||
if (line) {
|
||||
line = trimStart(line.trim(), '#').trim();
|
||||
if (line && line.length) {
|
||||
console.log('SerialOutput', line);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
port.on('error', (error: any) => {
|
||||
if (errorRejected) return;
|
||||
console.log(error);
|
||||
rejectIfError(error);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (errorRejected) return;
|
||||
// Prompt user to enter configuration mode
|
||||
|
||||
if (!gotData || !configMode) {
|
||||
vscode.window
|
||||
.showInformationMessage(
|
||||
'Please hold down button A and then push and release the reset button to enter configuration mode.')
|
||||
.then(() => {
|
||||
port.write(
|
||||
'\r\nhelp\r\n',
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
(error: any) => {
|
||||
rejectIfError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}, 10000);
|
||||
// Configure serial port callbacks
|
||||
port.on('open', () => {
|
||||
port.write(
|
||||
'\r\nhelp\r\n',
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(error: any) => {
|
||||
if (rejectIfError(error)) return;
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
port.on('data', (data: any) => {
|
||||
gotData = true;
|
||||
const output = data.toString().trim();
|
||||
|
||||
if (commandExecuted) return;
|
||||
if (output.includes('set_')) {
|
||||
commandExecuted = true;
|
||||
configMode = true;
|
||||
executeSetAzIoTHub()
|
||||
.then(() => resolve(true))
|
||||
.catch((error) => reject(error));
|
||||
} else {
|
||||
configMode = false;
|
||||
}
|
||||
|
||||
if (configMode) {
|
||||
forEach(output.split('\n'), line => {
|
||||
if (line) {
|
||||
line = trimStart(line.trim(), '#').trim();
|
||||
if (line && line.length) {
|
||||
console.log('SerialOutput', line);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
port.on('error', (error: any) => {
|
||||
if (errorRejected) return;
|
||||
console.log(error);
|
||||
rejectIfError(error);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (errorRejected) return;
|
||||
// Prompt user to enter configuration mode
|
||||
|
||||
if (!gotData || !configMode) {
|
||||
vscode.window
|
||||
.showInformationMessage(
|
||||
'Please hold down button A and then push and release the reset button to enter configuration mode.')
|
||||
.then(() => {
|
||||
port.write(
|
||||
'\r\nhelp\r\n',
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(error: any) => {
|
||||
rejectIfError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}, 10000);
|
||||
});
|
||||
}
|
||||
|
||||
private getComList(): Promise<SerialPortInfo[]> {
|
||||
return new Promise(
|
||||
(resolve: (value: SerialPortInfo[]) => void,
|
||||
reject: (error: Error) => void) => {
|
||||
// tslint:disable-next-line: no-any
|
||||
AZ3166Device.serialport.list((e: any, ports: SerialPortInfo[]) => {
|
||||
if (e) {
|
||||
reject(e);
|
||||
} else {
|
||||
resolve(ports);
|
||||
}
|
||||
});
|
||||
(resolve: (value: SerialPortInfo[]) => void,
|
||||
reject: (error: Error) => void) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
AZ3166Device.serialport.list((e: any, ports: SerialPortInfo[]) => {
|
||||
if (e) {
|
||||
reject(e);
|
||||
} else {
|
||||
resolve(ports);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async chooseCOM(): Promise<string> {
|
||||
return new Promise(
|
||||
async (
|
||||
resolve: (value: string) => void,
|
||||
reject: (reason: Error) => void) => {
|
||||
const comList = await this.getComList();
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
async (
|
||||
resolve: (value: string) => void,
|
||||
reject: (reason: Error) => void) => {
|
||||
const comList = await this.getComList();
|
||||
|
||||
const az3166 = this.board;
|
||||
const az3166 = this.board;
|
||||
|
||||
if (!az3166) {
|
||||
return reject(new Error('AZ3166 is not found in the board list.'));
|
||||
}
|
||||
if (!az3166) {
|
||||
return reject(new Error('AZ3166 is not found in the board list.'));
|
||||
}
|
||||
|
||||
const list = _.filter(comList, com => {
|
||||
if (com.vendorId && com.productId && az3166.vendorId &&
|
||||
const list = _.filter(comList, com => {
|
||||
if (com.vendorId && com.productId && az3166.vendorId &&
|
||||
az3166.productId &&
|
||||
com.vendorId.toLowerCase().endsWith(az3166.vendorId) &&
|
||||
com.productId.toLowerCase().endsWith(az3166.productId)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (list && list.length) {
|
||||
let comPort = list[0].comName;
|
||||
if (list.length > 1) {
|
||||
// TODO: select com port from list when there are multiple AZ3166
|
||||
// boards connected
|
||||
comPort = list[0].comName;
|
||||
}
|
||||
|
||||
if (!comPort) {
|
||||
reject(new Error('No avalible COM port.'));
|
||||
}
|
||||
|
||||
resolve(comPort);
|
||||
return true;
|
||||
} else {
|
||||
reject(new Error('No AZ3166 board connected.'));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (list && list.length) {
|
||||
let comPort = list[0].comName;
|
||||
if (list.length > 1) {
|
||||
// TODO: select com port from list when there are multiple AZ3166
|
||||
// boards connected
|
||||
comPort = list[0].comName;
|
||||
}
|
||||
|
||||
if (!comPort) {
|
||||
reject(new Error('No avalible COM port.'));
|
||||
}
|
||||
|
||||
resolve(comPort);
|
||||
} else {
|
||||
reject(new Error('No AZ3166 board connected.'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private async sendDataViaSerialPort(port: any, data: string):
|
||||
Promise<boolean> {
|
||||
return new Promise(
|
||||
(resolve: (value: boolean) => void, reject: (value: Error) => void) => {
|
||||
try {
|
||||
port.write(
|
||||
data,
|
||||
// tslint:disable-next-line: no-any
|
||||
(err: any) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
port.drain(() => resolve(true));
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
(resolve: (value: boolean) => void, reject: (value: Error) => void) => {
|
||||
try {
|
||||
port.write(
|
||||
data,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(err: any) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
port.drain(() => resolve(true));
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async stlinkDriverInstalled() {
|
||||
private async stlinkDriverInstalled(): Promise<boolean> {
|
||||
const platform = os.platform();
|
||||
if (platform === OSPlatform.WIN32) {
|
||||
try {
|
||||
// The STlink driver would write to the following registry.
|
||||
const pathString = await getRegistryValues(
|
||||
WinReg.HKLM,
|
||||
'\\SYSTEM\\ControlSet001\\Control\\Class\\{88bae032-5a81-49f0-bc3d-a4ff138216d6}',
|
||||
'Class');
|
||||
WinReg.HKLM,
|
||||
'\\SYSTEM\\ControlSet001\\Control\\Class\\{88bae032-5a81-49f0-bc3d-a4ff138216d6}',
|
||||
'Class');
|
||||
if (pathString) {
|
||||
return true;
|
||||
} else {
|
||||
|
@ -752,18 +762,18 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
return true;
|
||||
}
|
||||
|
||||
private async generatePlatformLocal() {
|
||||
private async generatePlatformLocal(): Promise<void> {
|
||||
const arduinoPackagePath = this.getArduinoPackagePath();
|
||||
|
||||
function getHashMacAsync() {
|
||||
function getHashMacAsync(): Promise<unknown> {
|
||||
return new Promise((resolve) => {
|
||||
getmac.getMac((err, macAddress) => {
|
||||
if (err) {
|
||||
throw (err);
|
||||
reject(err);
|
||||
}
|
||||
const hashMacAddress = crypto.createHash('sha256')
|
||||
.update(macAddress, 'utf8')
|
||||
.digest('hex');
|
||||
.update(macAddress, 'utf8')
|
||||
.digest('hex');
|
||||
resolve(hashMacAddress);
|
||||
});
|
||||
});
|
||||
|
@ -771,7 +781,7 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
|
||||
if (!fs.existsSync(arduinoPackagePath)) {
|
||||
throw new Error(
|
||||
'Unable to locate Arduino IDE. Please install it from https://www.arduino.cc/en/main/software and use "Arduino: Board Manager" to install your device packages. Restart VS Code to apply to changes.');
|
||||
'Unable to locate Arduino IDE. Please install it from https://www.arduino.cc/en/main/software and use "Arduino: Board Manager" to install your device packages. Restart VS Code to apply to changes.');
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(arduinoPackagePath);
|
||||
|
@ -783,38 +793,34 @@ export class AZ3166Device extends ArduinoDeviceBase {
|
|||
|
||||
if (files.length === 0 || files.length > 1) {
|
||||
throw new Error(
|
||||
'There are unexpected files or folders under Arduino package installation path. Please clear the folder and reinstall the package for Devkit.');
|
||||
'There are unexpected files or folders under Arduino package installation path. Please clear the folder and reinstall the package for Devkit.');
|
||||
}
|
||||
|
||||
const directoryName = path.join(arduinoPackagePath, files[0]);
|
||||
if (!fs.isDirectorySync(directoryName)) {
|
||||
throw new Error(
|
||||
'The Arduino package for MXChip IoT Devkit is not installed. Please follow the guide to install it');
|
||||
'The Arduino package for MXChip IoT Devkit is not installed. Please follow the guide to install it');
|
||||
}
|
||||
|
||||
const fileName = path.join(directoryName, constants.platformLocalFileName);
|
||||
if (!fs.existsSync(fileName)) {
|
||||
const enableTrace = 1;
|
||||
let hashMacAddress;
|
||||
hashMacAddress = await getHashMacAsync();
|
||||
const hashMacAddress = await getHashMacAsync();
|
||||
|
||||
// Create the file of platform.local.txt
|
||||
const targetFileName =
|
||||
path.join(directoryName, constants.platformLocalFileName);
|
||||
|
||||
const content = `${constants.cExtraFlag}${hashMacAddress}" ${
|
||||
constants.traceExtraFlag}${enableTrace}\r\n` +
|
||||
constants.traceExtraFlag}${enableTrace}\r\n` +
|
||||
`${constants.cppExtraFlag}${hashMacAddress}" ${
|
||||
constants.traceExtraFlag}${enableTrace}\r\n`;
|
||||
try {
|
||||
fs.writeFileSync(targetFileName, content);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
constants.traceExtraFlag}${enableTrace}\r\n`;
|
||||
fs.writeFileSync(targetFileName, content);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private getArduinoPackagePath() {
|
||||
private getArduinoPackagePath(): string {
|
||||
const platform = os.platform();
|
||||
|
||||
// TODO: Currently, we do not support portable Arduino installation.
|
||||
|
|
|
@ -3,27 +3,31 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {AzureAccount} from '../azure-account.api';
|
||||
import {AzureAccountCommands} from '../common/Commands';
|
||||
import { AzureAccount } from '../azure-account.api';
|
||||
import { AzureAccountCommands } from '../common/Commands';
|
||||
|
||||
import {ExtensionName} from './Interfaces/Api';
|
||||
import { ExtensionName } from './Interfaces/Api';
|
||||
|
||||
export function getExtension(name: ExtensionName) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function getExtension(name: ExtensionName): any {
|
||||
switch (name) {
|
||||
case ExtensionName.Toolkit:
|
||||
const toolkit =
|
||||
case ExtensionName.Toolkit:{
|
||||
const toolkit =
|
||||
vscode.extensions.getExtension('vsciot-vscode.azure-iot-toolkit');
|
||||
return toolkit ? toolkit.exports : undefined;
|
||||
case ExtensionName.AzureAccount:
|
||||
const azureAccount = vscode.extensions.getExtension<AzureAccount>(
|
||||
'ms-vscode.azure-account');
|
||||
return azureAccount ? azureAccount.exports : undefined;
|
||||
case ExtensionName.DigitalTwins:
|
||||
const digitalTwins =
|
||||
return toolkit ? toolkit.exports : undefined;
|
||||
}
|
||||
case ExtensionName.AzureAccount:{
|
||||
const azureAccount = vscode.extensions.getExtension<AzureAccount>(
|
||||
'ms-vscode.azure-account');
|
||||
return azureAccount ? azureAccount.exports : undefined;
|
||||
}
|
||||
case ExtensionName.DigitalTwins:{
|
||||
const digitalTwins =
|
||||
vscode.extensions.getExtension('vsciot-vscode.azure-digital-twins');
|
||||
return digitalTwins ? digitalTwins.exports : undefined;
|
||||
default:
|
||||
return undefined;
|
||||
return digitalTwins ? digitalTwins.exports : undefined;
|
||||
}
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +35,7 @@ export async function checkAzureLogin(): Promise<boolean> {
|
|||
const azureAccount = getExtension(ExtensionName.AzureAccount);
|
||||
if (!azureAccount) {
|
||||
throw new Error(
|
||||
'Azure account extension is not found. Please install it from Marketplace.');
|
||||
'Azure account extension is not found. Please install it from Marketplace.');
|
||||
}
|
||||
|
||||
// Sign in Azure
|
||||
|
|
|
@ -5,18 +5,18 @@ import * as fs from 'fs-plus';
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {VscodeCommands} from '../common/Commands';
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {ConfigKey, DependentExtensions, FileNames, OperationType, OSPlatform, PlatformType, ScaffoldType} from '../constants';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {TelemetryContext} from '../telemetry';
|
||||
import { VscodeCommands } from '../common/Commands';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { ConfigKey, DependentExtensions, FileNames, OperationType, OSPlatform, PlatformType, ScaffoldType } from '../constants';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { TelemetryContext } from '../telemetry';
|
||||
import * as utils from '../utils';
|
||||
|
||||
import {Board} from './Interfaces/Board';
|
||||
import {ComponentType} from './Interfaces/Component';
|
||||
import {Device, DeviceType} from './Interfaces/Device';
|
||||
import {TemplateFileInfo} from './Interfaces/ProjectTemplate';
|
||||
import {OTA} from './OTA';
|
||||
import { Board } from './Interfaces/Board';
|
||||
import { ComponentType } from './Interfaces/Component';
|
||||
import { Device, DeviceType } from './Interfaces/Device';
|
||||
import { TemplateFileInfo } from './Interfaces/ProjectTemplate';
|
||||
import { OTA } from './OTA';
|
||||
|
||||
const constants = {
|
||||
defaultSketchFileName: 'device.ino',
|
||||
|
@ -47,9 +47,9 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
abstract board: Board|undefined;
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, devicePath: string,
|
||||
channel: vscode.OutputChannel, telemetryContext: TelemetryContext,
|
||||
deviceType: DeviceType) {
|
||||
context: vscode.ExtensionContext, devicePath: string,
|
||||
channel: vscode.OutputChannel, telemetryContext: TelemetryContext,
|
||||
deviceType: DeviceType) {
|
||||
this.deviceType = deviceType;
|
||||
this.componentType = ComponentType.Device;
|
||||
this.deviceFolder = devicePath;
|
||||
|
@ -57,7 +57,7 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
this.vscodeFolderPath =
|
||||
path.join(this.deviceFolder, FileNames.vscodeSettingsFolderName);
|
||||
this.boardFolderPath = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
this.telemetryContext = telemetryContext;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
@ -73,13 +73,13 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
static async isAvailable(): Promise<boolean> {
|
||||
if (!vscode.extensions.getExtension(DependentExtensions.arduino)) {
|
||||
const choice = await vscode.window.showInformationMessage(
|
||||
'Arduino extension is required for the current project. Do you want to install it from marketplace?',
|
||||
'Yes', 'No');
|
||||
'Arduino extension is required for the current project. Do you want to install it from marketplace?',
|
||||
'Yes', 'No');
|
||||
if (choice === 'Yes') {
|
||||
vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen,
|
||||
vscode.Uri.parse(
|
||||
'vscode:extension/' + DependentExtensions.arduino));
|
||||
VscodeCommands.VscodeOpen,
|
||||
vscode.Uri.parse(
|
||||
'vscode:extension/' + DependentExtensions.arduino));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -102,9 +102,9 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
}
|
||||
|
||||
await utils.fetchAndExecuteTask(
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.deviceFolder, OperationType.Compile, PlatformType.Arduino,
|
||||
constants.compileTaskName);
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.deviceFolder, OperationType.Compile, PlatformType.Arduino,
|
||||
constants.compileTaskName);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -114,9 +114,9 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
return false;
|
||||
}
|
||||
await utils.fetchAndExecuteTask(
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.deviceFolder, OperationType.Upload, PlatformType.Arduino,
|
||||
constants.uploadTaskName);
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.deviceFolder, OperationType.Upload, PlatformType.Arduino,
|
||||
constants.uploadTaskName);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
|
||||
const loadTimeScaffoldType = ScaffoldType.Workspace;
|
||||
if (!await FileUtility.directoryExists(
|
||||
loadTimeScaffoldType, deviceFolderPath)) {
|
||||
loadTimeScaffoldType, deviceFolderPath)) {
|
||||
throw new Error('Unable to find the device folder inside the project.');
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
// Generate template files
|
||||
const createTimeScaffoldType = ScaffoldType.Local;
|
||||
if (!await FileUtility.directoryExists(
|
||||
createTimeScaffoldType, this.deviceFolder)) {
|
||||
createTimeScaffoldType, this.deviceFolder)) {
|
||||
throw new Error(`Internal error: Couldn't find the template folder.`);
|
||||
}
|
||||
if (!board) {
|
||||
|
@ -156,14 +156,14 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
|
||||
for (const fileInfo of templateFiles) {
|
||||
await utils.generateTemplateFile(
|
||||
this.deviceFolder, createTimeScaffoldType, fileInfo);
|
||||
this.deviceFolder, createTimeScaffoldType, fileInfo);
|
||||
}
|
||||
|
||||
await this.generateCppPropertiesFile(createTimeScaffoldType, board);
|
||||
|
||||
// Configurate device environment
|
||||
await this.configDeviceEnvironment(
|
||||
this.deviceFolder, createTimeScaffoldType);
|
||||
this.deviceFolder, createTimeScaffoldType);
|
||||
}
|
||||
|
||||
// Backward compatibility: Check configuration
|
||||
|
@ -174,7 +174,7 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
abstract get version(): string;
|
||||
|
||||
private async writeCppPropertiesFile(
|
||||
boardId: string, type: ScaffoldType, platform: string): Promise<void> {
|
||||
boardId: string, type: ScaffoldType, platform: string): Promise<void> {
|
||||
const cppPropertiesFilePath =
|
||||
path.join(this.vscodeFolderPath, constants.cppPropertiesFileName);
|
||||
|
||||
|
@ -200,8 +200,8 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
|
||||
const cppPropertiesTemplateFilePath =
|
||||
this.extensionContext.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
boardId, cppPropertiesTemplateFileName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
boardId, cppPropertiesTemplateFileName));
|
||||
const propertiesContent =
|
||||
await FileUtility.readFile(type, cppPropertiesTemplateFilePath);
|
||||
const propertiesContentString = propertiesContent.toString();
|
||||
|
@ -232,8 +232,7 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
}
|
||||
}
|
||||
|
||||
async generateCrc(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel) {
|
||||
async generateCrc(channel: vscode.OutputChannel): Promise<boolean> {
|
||||
if (!(vscode.workspace.workspaceFolders &&
|
||||
vscode.workspace.workspaceFolders.length > 0)) {
|
||||
const message = 'No workspace opened.';
|
||||
|
@ -250,8 +249,8 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
return false;
|
||||
}
|
||||
const deviceBuildLocation = path.join(
|
||||
vscode.workspace.workspaceFolders[0].uri.fsPath, '..', devicePath,
|
||||
'.build');
|
||||
vscode.workspace.workspaceFolders[0].uri.fsPath, '..', devicePath,
|
||||
'.build');
|
||||
|
||||
if (!deviceBuildLocation) {
|
||||
const message = 'No device compile output folder found.';
|
||||
|
@ -277,7 +276,7 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
const binFilePickItems: vscode.QuickPickItem[] = [];
|
||||
for (const file of binFiles) {
|
||||
const fileName = path.basename(file);
|
||||
binFilePickItems.push({label: fileName, description: file});
|
||||
binFilePickItems.push({ label: fileName, description: file });
|
||||
}
|
||||
|
||||
const choice = await vscode.window.showQuickPick(binFilePickItems, {
|
||||
|
@ -315,15 +314,15 @@ export abstract class ArduinoDeviceBase implements Device {
|
|||
}
|
||||
|
||||
async configDeviceEnvironment(
|
||||
deviceRootPath: string, scaffoldType: ScaffoldType): Promise<void> {
|
||||
deviceRootPath: string, scaffoldType: ScaffoldType): Promise<void> {
|
||||
if (!deviceRootPath) {
|
||||
throw new Error(
|
||||
'Unable to find the project device path, please open the folder and initialize project again.');
|
||||
'Unable to find the project device path, please open the folder and initialize project again.');
|
||||
}
|
||||
|
||||
const templateFilesInfo = await utils.getEnvTemplateFilesAndAskOverwrite(
|
||||
this.extensionContext, this.deviceFolder, scaffoldType,
|
||||
constants.environmentTemplateFolderName);
|
||||
this.extensionContext, this.deviceFolder, scaffoldType,
|
||||
constants.environmentTemplateFolderName);
|
||||
|
||||
// Configure project environment with template files
|
||||
for (const fileInfo of templateFilesInfo) {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import * as path from 'path';
|
||||
|
||||
import {AzureComponentsStorage, ScaffoldType} from '../constants';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import { AzureComponentsStorage, ScaffoldType } from '../constants';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
|
||||
import {Component} from './Interfaces/Component';
|
||||
import {ComponentType} from './Interfaces/Component';
|
||||
import { Component } from './Interfaces/Component';
|
||||
import { ComponentType } from './Interfaces/Component';
|
||||
|
||||
// TODO: need to check what value should be included here
|
||||
export interface ComponentInfo { values: {[key: string]: string}; }
|
||||
export interface ComponentInfo { values: {[key: string]: string} }
|
||||
|
||||
export enum DependencyType {
|
||||
Other,
|
||||
|
@ -29,7 +29,7 @@ export interface AzureComponentConfig {
|
|||
componentInfo?: ComponentInfo;
|
||||
}
|
||||
|
||||
export interface AzureConfigs { componentConfigs: AzureComponentConfig[]; }
|
||||
export interface AzureConfigs { componentConfigs: AzureComponentConfig[] }
|
||||
|
||||
export interface Dependency {
|
||||
component: Component;
|
||||
|
@ -43,12 +43,12 @@ export class AzureConfigFileHandler {
|
|||
constructor(projectRoot: string) {
|
||||
this.projectRootPath = projectRoot;
|
||||
this.configFilePath = path.join(
|
||||
this.projectRootPath, AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
this.projectRootPath, AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
}
|
||||
|
||||
async createIfNotExists(type: ScaffoldType) {
|
||||
const azureConfigs: AzureConfigs = {componentConfigs: []};
|
||||
async createIfNotExists(type: ScaffoldType): Promise<void> {
|
||||
const azureConfigs: AzureConfigs = { componentConfigs: [] };
|
||||
const azureConfigFolderPath =
|
||||
path.join(this.projectRootPath, AzureComponentsStorage.folderName);
|
||||
if (!await FileUtility.directoryExists(type, azureConfigFolderPath)) {
|
||||
|
@ -56,7 +56,7 @@ export class AzureConfigFileHandler {
|
|||
await FileUtility.mkdirRecursively(type, azureConfigFolderPath);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to create azure config folder. Error message: ${
|
||||
error.message}`);
|
||||
error.message}`);
|
||||
}
|
||||
}
|
||||
const azureConfigFilePath =
|
||||
|
@ -67,7 +67,7 @@ export class AzureConfigFileHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async getSortedComponents(type: ScaffoldType) {
|
||||
async getSortedComponents(type: ScaffoldType): Promise<AzureComponentConfig[]> {
|
||||
try {
|
||||
const azureConfigContent =
|
||||
await FileUtility.readFile(type, this.configFilePath, 'utf8');
|
||||
|
@ -108,7 +108,7 @@ export class AzureConfigFileHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async getComponentIndexById(type: ScaffoldType, id: string) {
|
||||
async getComponentIndexById(type: ScaffoldType, id: string): Promise<number> {
|
||||
try {
|
||||
const azureConfigContent =
|
||||
await FileUtility.readFile(type, this.configFilePath, 'utf8');
|
||||
|
@ -122,7 +122,7 @@ export class AzureConfigFileHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async getComponentById(type: ScaffoldType, id: string) {
|
||||
async getComponentById(type: ScaffoldType, id: string): Promise<AzureComponentConfig | undefined> {
|
||||
try {
|
||||
const azureConfigContent =
|
||||
await FileUtility.readFile(type, this.configFilePath, 'utf8');
|
||||
|
@ -136,7 +136,7 @@ export class AzureConfigFileHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async appendComponent(type: ScaffoldType, component: AzureComponentConfig) {
|
||||
async appendComponent(type: ScaffoldType, component: AzureComponentConfig): Promise<AzureConfigs> {
|
||||
try {
|
||||
const azureConfigContent =
|
||||
await FileUtility.readFile(type, this.configFilePath, 'utf8');
|
||||
|
@ -150,8 +150,7 @@ export class AzureConfigFileHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async updateComponent(
|
||||
type: ScaffoldType, index: number, componentInfo: ComponentInfo) {
|
||||
async updateComponent(type: ScaffoldType, index: number, componentInfo: ComponentInfo): Promise<AzureConfigs> {
|
||||
try {
|
||||
const azureConfigContent =
|
||||
await FileUtility.readFile(type, this.configFilePath, 'utf8');
|
||||
|
|
|
@ -9,23 +9,23 @@ import * as vscode from 'vscode';
|
|||
|
||||
import * as utils from '../utils';
|
||||
import WebSiteManagementClient = require('azure-arm-website');
|
||||
import {Component, ComponentType} from './Interfaces/Component';
|
||||
import {Provisionable} from './Interfaces/Provisionable';
|
||||
import {Deployable} from './Interfaces/Deployable';
|
||||
import { Component, ComponentType } from './Interfaces/Component';
|
||||
import { Provisionable } from './Interfaces/Provisionable';
|
||||
import { Deployable } from './Interfaces/Deployable';
|
||||
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {ConfigKey, AzureFunctionsLanguage, AzureComponentsStorage, DependentExtensions, ScaffoldType} from '../constants';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { ConfigKey, AzureFunctionsLanguage, AzureComponentsStorage, DependentExtensions, ScaffoldType } from '../constants';
|
||||
|
||||
import {ServiceClientCredentials} from 'ms-rest';
|
||||
import {AzureAccount, AzureResourceFilter} from '../azure-account.api';
|
||||
import {StringDictionary} from 'azure-arm-website/lib/models';
|
||||
import {getExtension} from './Apis';
|
||||
import {ExtensionName} from './Interfaces/Api';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import {AzureComponentConfig, AzureConfigs, ComponentInfo, DependencyConfig, Dependency} from './AzureComponentConfig';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {VscodeCommands, AzureFunctionsCommands} from '../common/Commands';
|
||||
import {CancelOperationError} from '../CancelOperationError';
|
||||
import { ServiceClientCredentials } from 'ms-rest';
|
||||
import { AzureAccount, AzureResourceFilter } from '../azure-account.api';
|
||||
import { StringDictionary } from 'azure-arm-website/lib/models';
|
||||
import { getExtension } from './Apis';
|
||||
import { ExtensionName } from './Interfaces/Api';
|
||||
import { Guid } from 'guid-typescript';
|
||||
import { AzureComponentConfig, AzureConfigs, ComponentInfo, DependencyConfig, Dependency } from './AzureComponentConfig';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { VscodeCommands, AzureFunctionsCommands } from '../common/Commands';
|
||||
import { CancelOperationError } from '../CancelOperationError';
|
||||
|
||||
const impor = require('impor')(__dirname);
|
||||
const azureUtilityModule =
|
||||
|
@ -42,7 +42,7 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
private functionFolder: string;
|
||||
|
||||
private componentId: string;
|
||||
get id() {
|
||||
get id(): string {
|
||||
return this.componentId;
|
||||
}
|
||||
|
||||
|
@ -69,9 +69,9 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
}
|
||||
|
||||
constructor(
|
||||
azureFunctionsPath: string, functionFolder: string,
|
||||
channel: vscode.OutputChannel, language: string|null = null,
|
||||
dependencyComponents: Dependency[]|null = null) {
|
||||
azureFunctionsPath: string, functionFolder: string,
|
||||
channel: vscode.OutputChannel, language: string|null = null,
|
||||
dependencyComponents: Dependency[]|null = null) {
|
||||
this.componentType = ComponentType.AzureFunctions;
|
||||
this.channel = channel;
|
||||
this.azureFunctionsPath = azureFunctionsPath;
|
||||
|
@ -80,8 +80,8 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
this.componentId = Guid.create().toString();
|
||||
if (dependencyComponents && dependencyComponents.length > 0) {
|
||||
dependencyComponents.forEach(
|
||||
dependency => this.dependencies.push(
|
||||
{id: dependency.component.id.toString(), type: dependency.type}));
|
||||
dependency => this.dependencies.push(
|
||||
{ id: dependency.component.id.toString(), type: dependency.type }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,13 +94,13 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
static async isAvailable(): Promise<boolean> {
|
||||
if (!vscode.extensions.getExtension(DependentExtensions.azureFunctions)) {
|
||||
const choice = await vscode.window.showInformationMessage(
|
||||
'Azure Functions extension is required for the current project. Do you want to install it from marketplace?',
|
||||
'Yes', 'No');
|
||||
'Azure Functions extension is required for the current project. Do you want to install it from marketplace?',
|
||||
'Yes', 'No');
|
||||
if (choice === 'Yes') {
|
||||
vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen,
|
||||
vscode.Uri.parse(
|
||||
'vscode:extension/' + DependentExtensions.azureFunctions));
|
||||
VscodeCommands.VscodeOpen,
|
||||
vscode.Uri.parse(
|
||||
'vscode:extension/' + DependentExtensions.azureFunctions));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -118,8 +118,8 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
|
||||
async load(): Promise<boolean> {
|
||||
const azureConfigFilePath = path.join(
|
||||
this.azureFunctionsPath, '..', AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
this.azureFunctionsPath, '..', AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
|
||||
if (!fs.existsSync(azureConfigFilePath)) {
|
||||
return false;
|
||||
|
@ -134,7 +134,7 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
}
|
||||
|
||||
const azureFunctionsConfig = azureConfigs.componentConfigs.find(
|
||||
config => config.folder === this.functionFolder);
|
||||
config => config.folder === this.functionFolder);
|
||||
if (azureFunctionsConfig) {
|
||||
this.componentId = azureFunctionsConfig.id;
|
||||
this.dependencies = azureFunctionsConfig.dependencies;
|
||||
|
@ -155,14 +155,14 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
|
||||
if (!await FileUtility.directoryExists(scaffoldType, azureFunctionsPath)) {
|
||||
throw new Error(
|
||||
'Unable to find the Azure Functions folder inside the project.');
|
||||
'Unable to find the Azure Functions folder inside the project.');
|
||||
}
|
||||
|
||||
if (!this.functionLanguage) {
|
||||
const picks: vscode.QuickPickItem[] = [
|
||||
{label: AzureFunctionsLanguage.CSharpScript, description: ''},
|
||||
{label: AzureFunctionsLanguage.JavaScript, description: ''},
|
||||
{label: AzureFunctionsLanguage.CSharpLibrary, description: ''}
|
||||
{ label: AzureFunctionsLanguage.CSharpScript, description: '' },
|
||||
{ label: AzureFunctionsLanguage.JavaScript, description: '' },
|
||||
{ label: AzureFunctionsLanguage.CSharpLibrary, description: '' }
|
||||
];
|
||||
|
||||
const languageSelection = await vscode.window.showQuickPick(picks, {
|
||||
|
@ -174,7 +174,7 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
|
||||
if (!languageSelection) {
|
||||
throw new CancelOperationError(
|
||||
'Unable to get the language for Azure Functions. Creating project for Azure Functions cancelled.');
|
||||
'Unable to get the language for Azure Functions. Creating project for Azure Functions cancelled.');
|
||||
}
|
||||
this.functionLanguage = languageSelection.label;
|
||||
}
|
||||
|
@ -183,32 +183,32 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
utils.getScriptTemplateNameFromLanguage(this.functionLanguage);
|
||||
if (!templateName) {
|
||||
throw new CancelOperationError(
|
||||
'Unable to get the template for Azure Functions.Creating project for Azure Functions cancelled.');
|
||||
'Unable to get the template for Azure Functions.Creating project for Azure Functions cancelled.');
|
||||
}
|
||||
|
||||
if (this.functionLanguage === AzureFunctionsLanguage.CSharpLibrary) {
|
||||
await vscode.commands.executeCommand(
|
||||
AzureFunctionsCommands.CreateNewProject, azureFunctionsPath,
|
||||
this.functionLanguage, '~2', false /* openFolder */, templateName,
|
||||
'IoTHubTrigger1', {
|
||||
connection: 'eventHubConnectionString',
|
||||
path: '%eventHubConnectionPath%',
|
||||
consumerGroup: '$Default',
|
||||
namespace: 'IoTWorkbench'
|
||||
});
|
||||
AzureFunctionsCommands.CreateNewProject, azureFunctionsPath,
|
||||
this.functionLanguage, '~2', false /* openFolder */, templateName,
|
||||
'IoTHubTrigger1', {
|
||||
connection: 'eventHubConnectionString',
|
||||
path: '%eventHubConnectionPath%',
|
||||
consumerGroup: '$Default',
|
||||
namespace: 'IoTWorkbench'
|
||||
});
|
||||
} else {
|
||||
await vscode.commands.executeCommand(
|
||||
AzureFunctionsCommands.CreateNewProject, azureFunctionsPath,
|
||||
this.functionLanguage, '~1', false /* openFolder */, templateName,
|
||||
'IoTHubTrigger1', {
|
||||
connection: 'eventHubConnectionString',
|
||||
path: '%eventHubConnectionPath%',
|
||||
consumerGroup: '$Default'
|
||||
});
|
||||
AzureFunctionsCommands.CreateNewProject, azureFunctionsPath,
|
||||
this.functionLanguage, '~1', false /* openFolder */, templateName,
|
||||
'IoTHubTrigger1', {
|
||||
connection: 'eventHubConnectionString',
|
||||
path: '%eventHubConnectionPath%',
|
||||
consumerGroup: '$Default'
|
||||
});
|
||||
}
|
||||
|
||||
await this.updateConfigSettings(
|
||||
scaffoldType, {values: {functionLanguage: this.functionLanguage}});
|
||||
scaffoldType, { values: { functionLanguage: this.functionLanguage } });
|
||||
}
|
||||
|
||||
async provision(): Promise<boolean> {
|
||||
|
@ -224,8 +224,8 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
|
||||
const functionAppId: string|undefined =
|
||||
await vscode.commands.executeCommand<string>(
|
||||
AzureFunctionsCommands.CreateFunctionApp, subscriptionId,
|
||||
resourceGroup);
|
||||
AzureFunctionsCommands.CreateFunctionApp, subscriptionId,
|
||||
resourceGroup);
|
||||
if (functionAppId) {
|
||||
await ConfigHandler.update(ConfigKey.functionAppId, functionAppId);
|
||||
const eventHubConnectionString =
|
||||
|
@ -245,13 +245,13 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
}
|
||||
|
||||
const resourceGroupMatches =
|
||||
functionAppId.match(/\/resourceGroups\/([^\/]*)/);
|
||||
functionAppId.match(/\/resourceGroups\/([^/]*)/);
|
||||
if (!resourceGroupMatches || resourceGroupMatches.length < 2) {
|
||||
throw new Error('Cannot parse resource group from function app ID.');
|
||||
}
|
||||
const resourceGroup = resourceGroupMatches[1];
|
||||
|
||||
const siteNameMatches = functionAppId.match(/\/sites\/([^\/]*)/);
|
||||
const siteNameMatches = functionAppId.match(/\/sites\/([^/]*)/);
|
||||
if (!siteNameMatches || siteNameMatches.length < 2) {
|
||||
throw new Error('Cannot parse function app name from function app ID.');
|
||||
}
|
||||
|
@ -282,12 +282,12 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
appSettings.properties['WEBSITE_RUN_FROM_PACKAGE'] = '0';
|
||||
|
||||
await client.webApps.updateApplicationSettings(
|
||||
resourceGroup, siteName, appSettings);
|
||||
resourceGroup, siteName, appSettings);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Creating Azure Functions application failed. Please check the error log in output window.');
|
||||
'Creating Azure Functions application failed. Please check the error log in output window.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +295,7 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
let deployPending: NodeJS.Timer|null = null;
|
||||
if (this.channel) {
|
||||
utils.channelShowAndAppendLine(
|
||||
this.channel, 'Deploying Azure Functions App...');
|
||||
this.channel, 'Deploying Azure Functions App...');
|
||||
deployPending = setInterval(() => {
|
||||
this.channel.append('.');
|
||||
}, 1000);
|
||||
|
@ -307,18 +307,16 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
if (this.functionLanguage !==
|
||||
AzureFunctionsLanguage.CSharpLibrary as string) {
|
||||
await vscode.commands.executeCommand(
|
||||
AzureFunctionsCommands.Deploy, azureFunctionsPath, functionAppId);
|
||||
AzureFunctionsCommands.Deploy, azureFunctionsPath, functionAppId);
|
||||
} else {
|
||||
const subPath =
|
||||
path.join(azureFunctionsPath, 'bin/Release/netcoreapp2.1/publish');
|
||||
await vscode.commands.executeCommand(
|
||||
AzureFunctionsCommands.Deploy, subPath, functionAppId);
|
||||
AzureFunctionsCommands.Deploy, subPath, functionAppId);
|
||||
}
|
||||
console.log(azureFunctionsPath, functionAppId);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
if (this.channel && deployPending) {
|
||||
clearInterval(deployPending);
|
||||
|
@ -330,10 +328,10 @@ export class AzureFunctions implements Component, Provisionable, Deployable {
|
|||
async updateConfigSettings(type: ScaffoldType, componentInfo?: ComponentInfo):
|
||||
Promise<void> {
|
||||
const azureConfigFilePath = path.join(
|
||||
this.azureFunctionsPath, '..', AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
this.azureFunctionsPath, '..', AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
|
||||
let azureConfigs: AzureConfigs = {componentConfigs: []};
|
||||
let azureConfigs: AzureConfigs = { componentConfigs: [] };
|
||||
|
||||
try {
|
||||
const azureConfigContent =
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import {ResourceManagementClient, ResourceModels, SubscriptionClient} from 'azure-arm-resource';
|
||||
import { ResourceManagementClient, ResourceModels, SubscriptionClient, SubscriptionModels } from 'azure-arm-resource';
|
||||
import * as fs from 'fs-plus';
|
||||
import {HttpMethods, WebResource} from 'ms-rest';
|
||||
import { HttpMethods, WebResource } from 'ms-rest';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {channelPrintJsonObject, channelShowAndAppendLine} from '../utils';
|
||||
import { channelPrintJsonObject, channelShowAndAppendLine } from '../utils';
|
||||
|
||||
import request = require('request-promise');
|
||||
import rq = require('request');
|
||||
|
||||
import {AzureAccount, AzureResourceFilter, AzureSession} from '../azure-account.api';
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import { AzureAccount, AzureResourceFilter, AzureSession } from '../azure-account.api';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
|
||||
import {getExtension} from './Apis';
|
||||
import {ExtensionName} from './Interfaces/Api';
|
||||
import {TelemetryWorker} from '../telemetry';
|
||||
import {EventNames} from '../constants';
|
||||
import { getExtension } from './Apis';
|
||||
import { ExtensionName } from './Interfaces/Api';
|
||||
import { TelemetryWorker } from '../telemetry';
|
||||
import { EventNames } from '../constants';
|
||||
|
||||
export interface ARMParameters {
|
||||
[key: string]: {value: string|number|boolean|null};
|
||||
|
@ -35,7 +35,9 @@ export interface ARMParameterTemplate {
|
|||
[key: string]: ARMParameterTemplateValue;
|
||||
}
|
||||
|
||||
export interface ARMTemplate { parameters: ARMParameterTemplate; }
|
||||
export interface ARMTemplate {
|
||||
parameters: ARMParameterTemplate;
|
||||
}
|
||||
|
||||
export class AzureUtility {
|
||||
private static _context: vscode.ExtensionContext;
|
||||
|
@ -46,8 +48,8 @@ export class AzureUtility {
|
|||
getExtension(ExtensionName.AzureAccount);
|
||||
|
||||
static init(
|
||||
context: vscode.ExtensionContext, channel?: vscode.OutputChannel,
|
||||
subscriptionId?: string) {
|
||||
context: vscode.ExtensionContext, channel?: vscode.OutputChannel,
|
||||
subscriptionId?: string): void {
|
||||
AzureUtility._context = context;
|
||||
AzureUtility._channel = channel;
|
||||
AzureUtility._subscriptionId = subscriptionId;
|
||||
|
@ -88,7 +90,7 @@ export class AzureUtility {
|
|||
const subscriptions: AzureResourceFilter[] =
|
||||
AzureUtility._azureAccountExtension.filters;
|
||||
const subscription = subscriptions.find(
|
||||
sub => sub.subscription.subscriptionId === subscriptionId);
|
||||
sub => sub.subscription.subscriptionId === subscriptionId);
|
||||
if (subscription) {
|
||||
return subscription.session;
|
||||
}
|
||||
|
@ -104,10 +106,10 @@ export class AzureUtility {
|
|||
}
|
||||
|
||||
return AzureUtility._getSessionBySubscriptionId(
|
||||
AzureUtility._subscriptionId);
|
||||
AzureUtility._subscriptionId);
|
||||
}
|
||||
|
||||
private static async _getResourceClient() {
|
||||
private static async _getResourceClient(): Promise<ResourceManagementClient | undefined> {
|
||||
AzureUtility._subscriptionId = await AzureUtility._getSubscription();
|
||||
|
||||
if (!AzureUtility._subscriptionId) {
|
||||
|
@ -118,38 +120,37 @@ export class AzureUtility {
|
|||
if (session) {
|
||||
const credential = session.credentials;
|
||||
const client = new ResourceManagementClient(
|
||||
credential, AzureUtility._subscriptionId,
|
||||
session.environment.resourceManagerEndpointUrl);
|
||||
credential, AzureUtility._subscriptionId,
|
||||
session.environment.resourceManagerEndpointUrl);
|
||||
return client;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static _getSubscriptionClientBySubscriptionId(substriptionId:
|
||||
string) {
|
||||
private static _getSubscriptionClientBySubscriptionId(substriptionId: string): ResourceManagementClient | undefined {
|
||||
const session = AzureUtility._getSessionBySubscriptionId(substriptionId);
|
||||
if (session) {
|
||||
const credential = session.credentials;
|
||||
const client = new ResourceManagementClient(
|
||||
credential, substriptionId,
|
||||
session.environment.resourceManagerEndpointUrl);
|
||||
credential, substriptionId,
|
||||
session.environment.resourceManagerEndpointUrl);
|
||||
return client;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static async _getSubscriptionClient() {
|
||||
private static async _getSubscriptionClient(): Promise<SubscriptionClient | undefined> {
|
||||
const session = await AzureUtility._getSession();
|
||||
if (session) {
|
||||
const credential = session.credentials;
|
||||
const client = new SubscriptionClient(
|
||||
credential, session.environment.resourceManagerEndpointUrl);
|
||||
credential, session.environment.resourceManagerEndpointUrl);
|
||||
return client;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static async _getLocations() {
|
||||
private static async _getLocations(): Promise<SubscriptionModels.LocationListResult | undefined> {
|
||||
AzureUtility._subscriptionId = await AzureUtility._getSubscription();
|
||||
|
||||
if (!AzureUtility._subscriptionId) {
|
||||
|
@ -166,7 +167,7 @@ export class AzureUtility {
|
|||
return locations;
|
||||
}
|
||||
|
||||
private static async _createResouceGroup() {
|
||||
private static async _createResouceGroup(): Promise<string | undefined> {
|
||||
const client = await AzureUtility._getResourceClient();
|
||||
if (!client) {
|
||||
return undefined;
|
||||
|
@ -176,7 +177,7 @@ export class AzureUtility {
|
|||
prompt: 'Input resouce group name',
|
||||
ignoreFocusOut: true,
|
||||
validateInput: async (name: string) => {
|
||||
if (!/^[a-z0-9_\-\.]*[a-z0-9_\-]+$/.test(name)) {
|
||||
if (!/^[a-z0-9_\-.]*[a-z0-9_-]+$/.test(name)) {
|
||||
return 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.';
|
||||
}
|
||||
|
||||
|
@ -206,33 +207,32 @@ export class AzureUtility {
|
|||
}
|
||||
|
||||
const resourceGroupLocation = await vscode.window.showQuickPick(
|
||||
locationList,
|
||||
{placeHolder: 'Select Resource Group Location', ignoreFocusOut: true});
|
||||
locationList,
|
||||
{ placeHolder: 'Select Resource Group Location', ignoreFocusOut: true });
|
||||
if (!resourceGroupLocation || !resourceGroupLocation.description) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const resourceGroup = await client.resourceGroups.createOrUpdate(
|
||||
resourceGroupName, {location: resourceGroupLocation.description});
|
||||
resourceGroupName, { location: resourceGroupLocation.description });
|
||||
|
||||
return resourceGroup.name;
|
||||
}
|
||||
|
||||
private static _commonParameterCheck(
|
||||
_value: string, parameter: ARMParameterTemplateValue) {
|
||||
private static _commonParameterCheck(_value: string, parameter: ARMParameterTemplateValue): string {
|
||||
let value: string|number|boolean|null = null;
|
||||
switch (parameter.type.toLocaleLowerCase()) {
|
||||
case 'string':
|
||||
value = _value;
|
||||
break;
|
||||
case 'int':
|
||||
value = Number(_value);
|
||||
break;
|
||||
case 'bool':
|
||||
value = _value.toLocaleLowerCase() === 'true';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case 'string':
|
||||
value = _value;
|
||||
break;
|
||||
case 'int':
|
||||
value = Number(_value);
|
||||
break;
|
||||
case 'bool':
|
||||
value = _value.toLocaleLowerCase() === 'true';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
|
@ -241,26 +241,26 @@ export class AzureUtility {
|
|||
|
||||
if (typeof value === 'string' && parameter.minLength !== undefined &&
|
||||
parameter.minLength > value.length) {
|
||||
return `The value does\'t meet requirement: minLength ${
|
||||
parameter.minLength}.`;
|
||||
return `The value does't meet requirement: minLength ${
|
||||
parameter.minLength}.`;
|
||||
}
|
||||
|
||||
if (typeof value === 'string' && parameter.maxLength !== undefined &&
|
||||
parameter.maxLength < value.length) {
|
||||
return `The value does\'t meet requirement: maxLength ${
|
||||
parameter.maxLength}.`;
|
||||
return `The value does't meet requirement: maxLength ${
|
||||
parameter.maxLength}.`;
|
||||
}
|
||||
|
||||
if (typeof value === 'number' && parameter.minValue !== undefined &&
|
||||
parameter.minValue > value) {
|
||||
return `The value does\'t meet requirement: minValue ${
|
||||
parameter.minValue}.`;
|
||||
return `The value does't meet requirement: minValue ${
|
||||
parameter.minValue}.`;
|
||||
}
|
||||
|
||||
if (typeof value === 'number' && parameter.maxValue !== undefined &&
|
||||
parameter.maxValue < value) {
|
||||
return `The value does\'t meet requirement: maxValue ${
|
||||
parameter.maxValue}.`;
|
||||
return `The value does't meet requirement: maxValue ${
|
||||
parameter.maxValue}.`;
|
||||
}
|
||||
|
||||
if (typeof value === 'number' && isNaN(value)) {
|
||||
|
@ -270,18 +270,17 @@ export class AzureUtility {
|
|||
return '';
|
||||
}
|
||||
|
||||
private static _getKeyDisplayName(key: string) {
|
||||
private static _getKeyDisplayName(key: string): string {
|
||||
key = key.replace(/^\$*/, '');
|
||||
const keyDisplayName = key.replace(/([A-Z][^A-Z])/g, ' $1')
|
||||
.replace(/([a-z])([A-Z])/g, '$1 $2');
|
||||
.replace(/([a-z])([A-Z])/g, '$1 $2');
|
||||
return keyDisplayName.substr(0, 1).toUpperCase() + keyDisplayName.substr(1);
|
||||
}
|
||||
|
||||
private static async _getARMParameters(
|
||||
parameterTemplate: ARMParameterTemplate, parameters?: ARMParameters) {
|
||||
private static async _getARMParameters(parameterTemplate: ARMParameterTemplate, parameters?: ARMParameters): Promise<ARMParameters|undefined> {
|
||||
parameters = parameters || {} as ARMParameters;
|
||||
for (const key of Object.keys(parameterTemplate)) {
|
||||
if (parameters.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(parameters, key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -294,7 +293,7 @@ export class AzureUtility {
|
|||
const values: vscode.QuickPickItem[] = [];
|
||||
for (const value of parameter.allowedValues) {
|
||||
if (value !== null) {
|
||||
values.push({label: value.toString(), description: ''});
|
||||
values.push({ label: value.toString(), description: '' });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,7 +314,7 @@ export class AzureUtility {
|
|||
} else {
|
||||
const _key = key.substr(2);
|
||||
const filePath = path.join(
|
||||
vscode.workspace.workspaceFolders[0].uri.fsPath, '..', _key);
|
||||
vscode.workspace.workspaceFolders[0].uri.fsPath, '..', _key);
|
||||
AzureUtility._context.asAbsolutePath(_key);
|
||||
if (fs.existsSync(filePath)) {
|
||||
inputValue = fs.readFileSync(filePath, 'utf8');
|
||||
|
@ -331,62 +330,63 @@ export class AzureUtility {
|
|||
ConfigHandler.get<string>('iothubConnectionString');
|
||||
|
||||
switch (_key) {
|
||||
case 'iotHubName':
|
||||
if (!iothubConnectionString) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
const iotHubNameMatches =
|
||||
case 'iotHubName':
|
||||
if (!iothubConnectionString) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
const iotHubNameMatches =
|
||||
iothubConnectionString.match(/HostName=(.*?)\./);
|
||||
if (!iotHubNameMatches) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
inputValue = iotHubNameMatches[1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'iotHubKeyName':
|
||||
if (!iothubConnectionString) {
|
||||
if (!iotHubNameMatches) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
const iotHubKeyNameMatches = iothubConnectionString.match(
|
||||
/SharedAccessKeyName=(.*?)(;|$)/);
|
||||
if (!iotHubKeyNameMatches) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
inputValue = iotHubKeyNameMatches[1];
|
||||
}
|
||||
inputValue = iotHubNameMatches[1];
|
||||
}
|
||||
break;
|
||||
case 'iotHubKey':
|
||||
if (!iothubConnectionString) {
|
||||
}
|
||||
break;
|
||||
case 'iotHubKeyName':
|
||||
if (!iothubConnectionString) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
const iotHubKeyNameMatches = iothubConnectionString.match(
|
||||
/SharedAccessKeyName=(.*?)(;|$)/);
|
||||
if (!iotHubKeyNameMatches) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
const iotHubKeyMatches =
|
||||
inputValue = iotHubKeyNameMatches[1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'iotHubKey':
|
||||
if (!iothubConnectionString) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
const iotHubKeyMatches =
|
||||
iothubConnectionString.match(/SharedAccessKey=(.*?)(;|$)/);
|
||||
if (!iotHubKeyMatches) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
inputValue = iotHubKeyMatches[1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'subscription':
|
||||
inputValue = AzureUtility._subscriptionId || '';
|
||||
break;
|
||||
default:
|
||||
const _value = ConfigHandler.get<string>(_key);
|
||||
if (!_value) {
|
||||
if (!iotHubKeyMatches) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
inputValue = _value;
|
||||
inputValue = iotHubKeyMatches[1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'subscription':
|
||||
inputValue = AzureUtility._subscriptionId || '';
|
||||
break;
|
||||
default:{
|
||||
const _value = ConfigHandler.get<string>(_key);
|
||||
if (!_value) {
|
||||
inputValue = '';
|
||||
} else {
|
||||
inputValue = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const _value = await vscode.window.showInputBox({
|
||||
prompt: `Input value for ${keyDisplayName}`,
|
||||
ignoreFocusOut: true,
|
||||
value: parameter.defaultValue ? parameter.defaultValue.toString() :
|
||||
'',
|
||||
'',
|
||||
validateInput: async (value: string) => {
|
||||
return AzureUtility._commonParameterCheck(value, parameter);
|
||||
}
|
||||
|
@ -400,33 +400,33 @@ export class AzureUtility {
|
|||
}
|
||||
|
||||
switch (parameter.type.toLocaleLowerCase()) {
|
||||
case 'string':
|
||||
value = inputValue;
|
||||
break;
|
||||
case 'int':
|
||||
value = Number(inputValue);
|
||||
break;
|
||||
case 'bool':
|
||||
value = inputValue.toLocaleLowerCase() === 'true';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case 'string':
|
||||
value = inputValue;
|
||||
break;
|
||||
case 'int':
|
||||
value = Number(inputValue);
|
||||
break;
|
||||
case 'bool':
|
||||
value = inputValue.toLocaleLowerCase() === 'true';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
parameters[key] = {value};
|
||||
parameters[key] = { value };
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static async _getSubscription() {
|
||||
private static async _getSubscription(): Promise<string | undefined> {
|
||||
if (AzureUtility._subscriptionId) {
|
||||
return AzureUtility._subscriptionId;
|
||||
}
|
||||
|
||||
const subscription = await vscode.window.showQuickPick(
|
||||
AzureUtility._getSubscriptionList(),
|
||||
{placeHolder: 'Select Subscription', ignoreFocusOut: true});
|
||||
AzureUtility._getSubscriptionList(),
|
||||
{ placeHolder: 'Select Subscription', ignoreFocusOut: true });
|
||||
if (!subscription || !subscription.description) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -437,14 +437,14 @@ export class AzureUtility {
|
|||
|
||||
try {
|
||||
telemetryWorker.sendEvent(
|
||||
EventNames.selectSubscription, telemetryContext);
|
||||
EventNames.selectSubscription, telemetryContext);
|
||||
} catch {
|
||||
// If sending telemetry failed, skip the error to avoid blocking user.
|
||||
}
|
||||
return subscription.description;
|
||||
}
|
||||
|
||||
private static async _getResourceGroupItems() {
|
||||
private static async _getResourceGroupItems(): Promise<vscode.QuickPickItem[]> {
|
||||
const client = await AzureUtility._getResourceClient();
|
||||
|
||||
if (!client) {
|
||||
|
@ -452,7 +452,7 @@ export class AzureUtility {
|
|||
}
|
||||
|
||||
const resourceGrouplist: vscode.QuickPickItem[] =
|
||||
[{label: '$(plus) Create Resource Group', description: '', detail: ''}];
|
||||
[{ label: '$(plus) Create Resource Group', description: '', detail: '' }];
|
||||
|
||||
const resourceGroups = await client.resourceGroups.list();
|
||||
|
||||
|
@ -467,7 +467,7 @@ export class AzureUtility {
|
|||
return resourceGrouplist;
|
||||
}
|
||||
|
||||
static async getResourceGroup() {
|
||||
static async getResourceGroup(): Promise<string | undefined> {
|
||||
const client = await AzureUtility._getResourceClient();
|
||||
|
||||
if (!client) {
|
||||
|
@ -476,8 +476,8 @@ export class AzureUtility {
|
|||
}
|
||||
|
||||
const choice = await vscode.window.showQuickPick(
|
||||
AzureUtility._getResourceGroupItems(),
|
||||
{placeHolder: 'Select Resource Group', ignoreFocusOut: true});
|
||||
AzureUtility._getResourceGroupItems(),
|
||||
{ placeHolder: 'Select Resource Group', ignoreFocusOut: true });
|
||||
|
||||
if (!choice) {
|
||||
AzureUtility._resourceGroup = undefined;
|
||||
|
@ -494,8 +494,7 @@ export class AzureUtility {
|
|||
}
|
||||
}
|
||||
|
||||
static async deployARMTemplate(
|
||||
template: ARMTemplate, parameters?: ARMParameters) {
|
||||
static async deployARMTemplate(template: ARMTemplate, parameters?: ARMParameters): Promise<ResourceModels.DeploymentExtended | undefined> {
|
||||
const client = await AzureUtility._getResourceClient();
|
||||
if (!client) {
|
||||
return undefined;
|
||||
|
@ -522,12 +521,12 @@ export class AzureUtility {
|
|||
|
||||
const mode = 'Incremental';
|
||||
const deploymentParameters:
|
||||
ResourceModels.Deployment = {properties: {parameters, template, mode}};
|
||||
ResourceModels.Deployment = { properties: { parameters, template, mode } };
|
||||
|
||||
try {
|
||||
const deployment = await client.deployments.createOrUpdate(
|
||||
AzureUtility._resourceGroup,
|
||||
`IoTWorkbecnhDeploy${new Date().getTime()}`, deploymentParameters);
|
||||
AzureUtility._resourceGroup,
|
||||
`IoTWorkbecnhDeploy${new Date().getTime()}`, deploymentParameters);
|
||||
|
||||
if (AzureUtility._channel && deployPending) {
|
||||
clearInterval(deployPending);
|
||||
|
@ -545,21 +544,21 @@ export class AzureUtility {
|
|||
}
|
||||
}
|
||||
|
||||
static get subscriptionId() {
|
||||
static get subscriptionId(): string|undefined {
|
||||
return AzureUtility._subscriptionId;
|
||||
}
|
||||
|
||||
static get resourceGroup() {
|
||||
static get resourceGroup(): string|undefined {
|
||||
return AzureUtility._resourceGroup;
|
||||
}
|
||||
|
||||
static getClient() {
|
||||
static getClient(): ResourceManagementClient |undefined {
|
||||
if (!AzureUtility._subscriptionId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const client = AzureUtility._getSubscriptionClientBySubscriptionId(
|
||||
AzureUtility._subscriptionId);
|
||||
AzureUtility._subscriptionId);
|
||||
if (!client) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -567,9 +566,8 @@ export class AzureUtility {
|
|||
return client;
|
||||
}
|
||||
|
||||
static async request(
|
||||
// tslint:disable-next-line: no-any
|
||||
method: HttpMethods, resource: string, body: any = null) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static async request(method: HttpMethods, resource: string, body: any = null): Promise<unknown> {
|
||||
const session = await AzureUtility._getSession();
|
||||
if (!session) {
|
||||
return undefined;
|
||||
|
@ -589,7 +587,7 @@ export class AzureUtility {
|
|||
httpRequestOption.simple = false;
|
||||
httpRequestOption.json = true;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
credential.signRequest(httpRequest, async err => {
|
||||
if (!err) {
|
||||
const res = await request(httpRequestOption);
|
||||
|
@ -601,12 +599,12 @@ export class AzureUtility {
|
|||
});
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
static async postRequest(resource: string, body: any = null) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static async postRequest(resource: string, body: any = null): Promise<unknown> {
|
||||
return AzureUtility.request('POST', resource, body);
|
||||
}
|
||||
|
||||
static async getRequest(resource: string) {
|
||||
static async getRequest(resource: string): Promise<unknown> {
|
||||
return AzureUtility.request('GET', resource);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import {Guid} from 'guid-typescript';
|
||||
import { Guid } from 'guid-typescript';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {CancelOperationError} from '../CancelOperationError';
|
||||
import {FileNames, OperationType, PlatformType, ScaffoldType, TemplateTag} from '../constants';
|
||||
import {DigitalTwinConstants} from '../DigitalTwin/DigitalTwinConstants';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {TelemetryContext} from '../telemetry';
|
||||
import { CancelOperationError } from '../CancelOperationError';
|
||||
import { FileNames, OperationType, PlatformType, ScaffoldType, TemplateTag } from '../constants';
|
||||
import { DigitalTwinConstants } from '../DigitalTwin/DigitalTwinConstants';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { TelemetryContext } from '../telemetry';
|
||||
import * as utils from '../utils';
|
||||
|
||||
import {ComponentType} from './Interfaces/Component';
|
||||
import {Device, DeviceType} from './Interfaces/Device';
|
||||
import {ProjectTemplate, TemplateFileInfo, TemplatesType} from './Interfaces/ProjectTemplate';
|
||||
import {RemoteExtension} from './RemoteExtension';
|
||||
import { ComponentType } from './Interfaces/Component';
|
||||
import { Device, DeviceType } from './Interfaces/Device';
|
||||
import { ProjectTemplate, TemplateFileInfo, TemplatesType } from './Interfaces/ProjectTemplate';
|
||||
import { RemoteExtension } from './RemoteExtension';
|
||||
|
||||
const constants = {
|
||||
configFile: 'config.json',
|
||||
|
@ -24,7 +24,7 @@ const constants = {
|
|||
|
||||
export abstract class ContainerDeviceBase implements Device {
|
||||
protected componentId: string;
|
||||
get id() {
|
||||
get id(): string {
|
||||
return this.componentId;
|
||||
}
|
||||
protected deviceType: DeviceType;
|
||||
|
@ -39,9 +39,9 @@ export abstract class ContainerDeviceBase implements Device {
|
|||
name = 'container base';
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, projectPath: string,
|
||||
channel: vscode.OutputChannel, telemetryContext: TelemetryContext,
|
||||
deviceType: DeviceType,
|
||||
context: vscode.ExtensionContext, projectPath: string,
|
||||
channel: vscode.OutputChannel, telemetryContext: TelemetryContext,
|
||||
deviceType: DeviceType,
|
||||
protected templateFilesInfo: TemplateFileInfo[] = []) {
|
||||
this.deviceType = deviceType;
|
||||
this.componentType = ComponentType.Device;
|
||||
|
@ -79,20 +79,20 @@ export abstract class ContainerDeviceBase implements Device {
|
|||
// ScaffoldType is local when creating a project
|
||||
const createTimeScaffoldType = ScaffoldType.Local;
|
||||
if (!await FileUtility.directoryExists(
|
||||
createTimeScaffoldType, this.projectFolder)) {
|
||||
createTimeScaffoldType, this.projectFolder)) {
|
||||
throw new Error('Unable to find the project folder.');
|
||||
}
|
||||
|
||||
await this.generateTemplateFiles(
|
||||
createTimeScaffoldType, this.projectFolder, this.templateFilesInfo);
|
||||
createTimeScaffoldType, this.projectFolder, this.templateFilesInfo);
|
||||
|
||||
await this.configDeviceEnvironment(
|
||||
this.projectFolder, createTimeScaffoldType);
|
||||
this.projectFolder, createTimeScaffoldType);
|
||||
}
|
||||
|
||||
async generateTemplateFiles(
|
||||
type: ScaffoldType, projectPath: string,
|
||||
templateFilesInfo: TemplateFileInfo[]): Promise<boolean> {
|
||||
type: ScaffoldType, projectPath: string,
|
||||
templateFilesInfo: TemplateFileInfo[]): Promise<boolean> {
|
||||
if (!templateFilesInfo) {
|
||||
throw new Error('No template file provided.');
|
||||
}
|
||||
|
@ -122,14 +122,14 @@ export abstract class ContainerDeviceBase implements Device {
|
|||
const isRemote = RemoteExtension.isRemote(this.extensionContext);
|
||||
if (!isRemote) {
|
||||
await utils.askAndOpenInRemote(
|
||||
OperationType.Compile, this.telemetryContext);
|
||||
OperationType.Compile, this.telemetryContext);
|
||||
return false;
|
||||
}
|
||||
|
||||
await utils.fetchAndExecuteTask(
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.projectFolder, OperationType.Compile, PlatformType.EmbeddedLinux,
|
||||
constants.compileTaskName);
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.projectFolder, OperationType.Compile, PlatformType.EmbeddedLinux,
|
||||
constants.compileTaskName);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -138,19 +138,19 @@ export abstract class ContainerDeviceBase implements Device {
|
|||
abstract async configDeviceSettings(): Promise<boolean>;
|
||||
|
||||
async configDeviceEnvironment(
|
||||
projectPath: string, scaffoldType: ScaffoldType): Promise<void> {
|
||||
projectPath: string, scaffoldType: ScaffoldType): Promise<void> {
|
||||
if (!projectPath) {
|
||||
throw new Error(
|
||||
'Unable to find the project path, please open the folder and initialize project again.');
|
||||
'Unable to find the project path, please open the folder and initialize project again.');
|
||||
}
|
||||
|
||||
// Get template list json object
|
||||
const templateJsonFilePath = this.extensionContext.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.templateFileName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.templateFileName));
|
||||
const templateJsonFileString =
|
||||
await FileUtility.readFile(
|
||||
scaffoldType, templateJsonFilePath, 'utf8') as string;
|
||||
scaffoldType, templateJsonFilePath, 'utf8') as string;
|
||||
const templateJson = JSON.parse(templateJsonFileString);
|
||||
if (!templateJson) {
|
||||
throw new Error('Fail to load template list.');
|
||||
|
@ -164,11 +164,11 @@ export abstract class ContainerDeviceBase implements Device {
|
|||
const templateName = containerSelection.label;
|
||||
if (!templateName) {
|
||||
throw new Error(
|
||||
`Internal Error: Cannot get template name from template property.`);
|
||||
`Internal Error: Cannot get template name from template property.`);
|
||||
}
|
||||
|
||||
const templateFilesInfo = await utils.getEnvTemplateFilesAndAskOverwrite(
|
||||
this.extensionContext, this.projectFolder, scaffoldType, templateName);
|
||||
this.extensionContext, this.projectFolder, scaffoldType, templateName);
|
||||
if (templateFilesInfo.length === 0) {
|
||||
throw new Error(`Internal Error: template files info is empty.`);
|
||||
}
|
||||
|
@ -197,13 +197,13 @@ export abstract class ContainerDeviceBase implements Device {
|
|||
const containerTemplates =
|
||||
templateListJson.templates.filter((template: ProjectTemplate) => {
|
||||
return (
|
||||
template.tag === TemplateTag.DevelopmentEnvironment &&
|
||||
template.tag === TemplateTag.DevelopmentEnvironment &&
|
||||
template.platform === PlatformType.EmbeddedLinux);
|
||||
});
|
||||
|
||||
const containerList: vscode.QuickPickItem[] = [];
|
||||
containerTemplates.forEach((container: ProjectTemplate) => {
|
||||
containerList.push({label: container.name, detail: container.detail});
|
||||
containerList.push({ label: container.name, detail: container.detail });
|
||||
});
|
||||
|
||||
const containerSelection =
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import * as crypto from 'crypto';
|
||||
import * as fs from 'fs-plus';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import { Guid } from 'guid-typescript';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import request = require('request-promise');
|
||||
import rq = require('request');
|
||||
|
||||
import {AzureComponentsStorage, FileNames, ScaffoldType} from '../constants';
|
||||
import { AzureComponentsStorage, FileNames, ScaffoldType } from '../constants';
|
||||
|
||||
import {AzureComponentConfig, AzureConfigFileHandler, AzureConfigs, ComponentInfo, Dependency, DependencyConfig, DependencyType} from './AzureComponentConfig';
|
||||
import {ARMTemplate, AzureUtility} from './AzureUtility';
|
||||
import {Component, ComponentType} from './Interfaces/Component';
|
||||
import {Provisionable} from './Interfaces/Provisionable';
|
||||
import {channelShowAndAppendLine, channelPrintJsonObject} from '../utils';
|
||||
import { AzureComponentConfig, AzureConfigFileHandler, AzureConfigs, ComponentInfo, Dependency, DependencyConfig, DependencyType } from './AzureComponentConfig';
|
||||
import { ARMTemplate, AzureUtility } from './AzureUtility';
|
||||
import { Component, ComponentType } from './Interfaces/Component';
|
||||
import { Provisionable } from './Interfaces/Provisionable';
|
||||
import { channelShowAndAppendLine, channelPrintJsonObject } from '../utils';
|
||||
|
||||
export class CosmosDB implements Component, Provisionable {
|
||||
dependencies: DependencyConfig[] = [];
|
||||
|
@ -24,14 +24,14 @@ export class CosmosDB implements Component, Provisionable {
|
|||
private azureConfigHandler: AzureConfigFileHandler;
|
||||
private extensionContext: vscode.ExtensionContext;
|
||||
private catchedCosmosDbList: Array<{name: string}> = [];
|
||||
get id() {
|
||||
get id(): string {
|
||||
return this.componentId;
|
||||
}
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, projectRoot: string,
|
||||
channel: vscode.OutputChannel,
|
||||
dependencyComponents: Dependency[]|null = null) {
|
||||
context: vscode.ExtensionContext, projectRoot: string,
|
||||
channel: vscode.OutputChannel,
|
||||
dependencyComponents: Dependency[]|null = null) {
|
||||
this.componentType = ComponentType.CosmosDB;
|
||||
this.channel = channel;
|
||||
this.componentId = Guid.create().toString();
|
||||
|
@ -40,8 +40,8 @@ export class CosmosDB implements Component, Provisionable {
|
|||
this.extensionContext = context;
|
||||
if (dependencyComponents && dependencyComponents.length > 0) {
|
||||
dependencyComponents.forEach(
|
||||
dependency => this.dependencies.push(
|
||||
{id: dependency.component.id, type: dependency.type}));
|
||||
dependency => this.dependencies.push(
|
||||
{ id: dependency.component.id, type: dependency.type }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,8 +57,8 @@ export class CosmosDB implements Component, Provisionable {
|
|||
|
||||
async load(): Promise<boolean> {
|
||||
const azureConfigFilePath = path.join(
|
||||
this.projectRootPath, AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
this.projectRootPath, AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
|
||||
if (!fs.existsSync(azureConfigFilePath)) {
|
||||
return false;
|
||||
|
@ -69,7 +69,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
try {
|
||||
azureConfigs = JSON.parse(fs.readFileSync(azureConfigFilePath, 'utf8'));
|
||||
const cosmosDBConfig = azureConfigs.componentConfigs.find(
|
||||
config => config.type === this.componentType);
|
||||
config => config.type === this.componentType);
|
||||
if (cosmosDBConfig) {
|
||||
this.componentId = cosmosDBConfig.id;
|
||||
this.dependencies = cosmosDBConfig.dependencies;
|
||||
|
@ -94,7 +94,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
return;
|
||||
}
|
||||
await this.azureConfigHandler.updateComponent(
|
||||
type, cosmosDBComponentIndex, componentInfo);
|
||||
type, cosmosDBComponentIndex, componentInfo);
|
||||
} else {
|
||||
const newCosmosDBConfig: AzureComponentConfig = {
|
||||
id: this.id,
|
||||
|
@ -110,7 +110,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
async provision(): Promise<boolean> {
|
||||
const cosmosDbList = this.getCosmosDbInResourceGroup();
|
||||
const cosmosDbNameChoose = await vscode.window.showQuickPick(
|
||||
cosmosDbList, {placeHolder: 'Select Cosmos DB', ignoreFocusOut: true});
|
||||
cosmosDbList, { placeHolder: 'Select Cosmos DB', ignoreFocusOut: true });
|
||||
if (!cosmosDbNameChoose) {
|
||||
return false;
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
channelShowAndAppendLine(this.channel, 'Creating Cosmos DB...');
|
||||
}
|
||||
const cosmosDBArmTemplatePath = this.extensionContext.asAbsolutePath(
|
||||
path.join(FileNames.resourcesFolderName, 'arm', 'cosmosdb.json'));
|
||||
path.join(FileNames.resourcesFolderName, 'arm', 'cosmosdb.json'));
|
||||
const cosmosDBArmTemplate =
|
||||
JSON.parse(fs.readFileSync(cosmosDBArmTemplatePath, 'utf8')) as
|
||||
ARMTemplate;
|
||||
|
@ -141,7 +141,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
|
||||
for (const dependency of this.dependencies) {
|
||||
const componentConfig = await this.azureConfigHandler.getComponentById(
|
||||
scaffoldType, dependency.id);
|
||||
scaffoldType, dependency.id);
|
||||
if (!componentConfig) {
|
||||
throw new Error(`Cannot find component with id ${dependency.id}.`);
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
|
||||
const databaseList = this.getDatabases(cosmosDbName, cosmosDbKey);
|
||||
const databaseChoose = await vscode.window.showQuickPick(
|
||||
databaseList, {placeHolder: 'Select Database', ignoreFocusOut: true});
|
||||
databaseList, { placeHolder: 'Select Database', ignoreFocusOut: true });
|
||||
if (!databaseChoose) {
|
||||
return false;
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
if (!value) {
|
||||
return 'Please fill this field.';
|
||||
}
|
||||
if (!/^[^\\\/#\?]+/.test(value)) {
|
||||
if (!/^[^\\/#?]+/.test(value)) {
|
||||
return 'May not end with space nor contain "\\", "/", "#", "?".';
|
||||
}
|
||||
return;
|
||||
|
@ -209,8 +209,8 @@ export class CosmosDB implements Component, Provisionable {
|
|||
const collectionList =
|
||||
this.getCollections(cosmosDbName, cosmosDbKey, database);
|
||||
const collectionChoose = await vscode.window.showQuickPick(
|
||||
collectionList,
|
||||
{placeHolder: 'Select Collection', ignoreFocusOut: true});
|
||||
collectionList,
|
||||
{ placeHolder: 'Select Collection', ignoreFocusOut: true });
|
||||
if (!collectionChoose) {
|
||||
return false;
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
if (!value) {
|
||||
return 'Please fill this field.';
|
||||
}
|
||||
if (!/^[^\\\/#\?]+/.test(value)) {
|
||||
if (!/^[^\\/#?]+/.test(value)) {
|
||||
return 'May not end with space nor contain "\\", "/", "#", "?".';
|
||||
}
|
||||
return;
|
||||
|
@ -238,7 +238,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
}
|
||||
collection = collection.trim();
|
||||
const cosmosDBApiRes = await this.ensureCollection(
|
||||
cosmosDbName, cosmosDbKey, database, collection);
|
||||
cosmosDbName, cosmosDbKey, database, collection);
|
||||
if (!cosmosDBApiRes) {
|
||||
throw new Error('Error occurred when create collection.');
|
||||
}
|
||||
|
@ -263,9 +263,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
return true;
|
||||
}
|
||||
|
||||
private _getCosmosDBAuthorizationToken(
|
||||
key: string, verb: string, date: string, resourceType: string,
|
||||
resourceId: string) {
|
||||
private _getCosmosDBAuthorizationToken(key: string, verb: string, date: string, resourceType: string, resourceId: string): string {
|
||||
const _key = Buffer.from(key, 'base64');
|
||||
const stringToSign =
|
||||
(`${verb}\n${resourceType}\n${resourceId}\n${date}\n\n`).toLowerCase();
|
||||
|
@ -278,14 +276,14 @@ export class CosmosDB implements Component, Provisionable {
|
|||
const tokenVersion = '1.0';
|
||||
|
||||
return encodeURIComponent(
|
||||
`type=${masterToken}&ver=${tokenVersion}&sig=${signature}`);
|
||||
`type=${masterToken}&ver=${tokenVersion}&sig=${signature}`);
|
||||
}
|
||||
|
||||
private _getRestHeaders(
|
||||
key: string, verb: string, resourceType: string, resourceId: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private _getRestHeaders(key: string, verb: string, resourceType: string, resourceId: string): any {
|
||||
const date = new Date().toUTCString();
|
||||
const authorization = this._getCosmosDBAuthorizationToken(
|
||||
key, verb, date, resourceType, resourceId);
|
||||
key, verb, date, resourceType, resourceId);
|
||||
const headers = {
|
||||
'Authorization': authorization,
|
||||
'Content-Type': 'application/json',
|
||||
|
@ -296,10 +294,7 @@ export class CosmosDB implements Component, Provisionable {
|
|||
return headers;
|
||||
}
|
||||
|
||||
private async _apiRequest(
|
||||
account: string, key: string, verb: string, path: string,
|
||||
resourceType: string, resourceId: string,
|
||||
body: {id: string}|null = null) {
|
||||
private async _apiRequest(account: string, key: string, verb: string, path: string, resourceType: string, resourceId: string, body: {id: string}|null = null): Promise<rq.Response> {
|
||||
const apiUrl = `https://${account}.documents.azure.com/${path}`;
|
||||
const headers = this._getRestHeaders(key, verb, resourceType, resourceId);
|
||||
const apiRes: rq.Response = await request({
|
||||
|
@ -319,28 +314,28 @@ export class CosmosDB implements Component, Provisionable {
|
|||
return apiRes;
|
||||
}
|
||||
|
||||
async getDatabases(account: string, key: string) {
|
||||
async getDatabases(account: string, key: string): Promise<vscode.QuickPickItem[]> {
|
||||
const getDatabasesRes =
|
||||
await this._apiRequest(account, key, 'GET', 'dbs', 'dbs', '');
|
||||
const listRes = getDatabasesRes.body as {Databases: Array<{id: string}>};
|
||||
const databaseList: vscode.QuickPickItem[] =
|
||||
[{label: '$(plus) Create New Database', description: ''}];
|
||||
[{ label: '$(plus) Create New Database', description: '' }];
|
||||
for (const item of listRes.Databases) {
|
||||
databaseList.push({label: item.id, description: account});
|
||||
databaseList.push({ label: item.id, description: account });
|
||||
}
|
||||
|
||||
return databaseList;
|
||||
}
|
||||
|
||||
async ensureDatabase(account: string, key: string, database: string) {
|
||||
async ensureDatabase(account: string, key: string, database: string): Promise<boolean> {
|
||||
const getDatabaseRes = await this._apiRequest(
|
||||
account, key, 'GET', `dbs/${database}`, 'dbs', `dbs/${database}`);
|
||||
account, key, 'GET', `dbs/${database}`, 'dbs', `dbs/${database}`);
|
||||
if (getDatabaseRes.statusCode === 200) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const createDatabaseRes = await this._apiRequest(
|
||||
account, key, 'POST', 'dbs', 'dbs', '', {id: database});
|
||||
account, key, 'POST', 'dbs', 'dbs', '', { id: database });
|
||||
if (createDatabaseRes.statusCode === 201) {
|
||||
return true;
|
||||
}
|
||||
|
@ -348,34 +343,33 @@ export class CosmosDB implements Component, Provisionable {
|
|||
return false;
|
||||
}
|
||||
|
||||
async getCollections(account: string, key: string, database: string) {
|
||||
async getCollections(account: string, key: string, database: string): Promise<vscode.QuickPickItem[]> {
|
||||
const getDCollectionsRes = await this._apiRequest(
|
||||
account, key, 'GET', `dbs/${database}/colls`, 'colls',
|
||||
`dbs/${database}`);
|
||||
account, key, 'GET', `dbs/${database}/colls`, 'colls',
|
||||
`dbs/${database}`);
|
||||
const listRes =
|
||||
getDCollectionsRes.body as {DocumentCollections: Array<{id: string}>};
|
||||
const collectionList: vscode.QuickPickItem[] =
|
||||
[{label: '$(plus) Create New Collection', description: ''}];
|
||||
[{ label: '$(plus) Create New Collection', description: '' }];
|
||||
for (const item of listRes.DocumentCollections) {
|
||||
collectionList.push(
|
||||
{label: item.id, description: `${account}/${database}`});
|
||||
{ label: item.id, description: `${account}/${database}` });
|
||||
}
|
||||
|
||||
return collectionList;
|
||||
}
|
||||
|
||||
async ensureCollection(
|
||||
account: string, key: string, database: string, collection: string) {
|
||||
async ensureCollection(account: string, key: string, database: string, collection: string): Promise<boolean> {
|
||||
const getCollectionRes = await this._apiRequest(
|
||||
account, key, 'GET', `dbs/${database}/colls/${collection}`, 'colls',
|
||||
`dbs/${database}/colls/${collection}`);
|
||||
account, key, 'GET', `dbs/${database}/colls/${collection}`, 'colls',
|
||||
`dbs/${database}/colls/${collection}`);
|
||||
if (getCollectionRes.statusCode === 200) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const creatCollectionRes = await this._apiRequest(
|
||||
account, key, 'POST', `dbs/${database}/colls`, 'colls',
|
||||
`dbs/${database}`, {id: collection});
|
||||
account, key, 'POST', `dbs/${database}/colls`, 'colls',
|
||||
`dbs/${database}`, { id: collection });
|
||||
if (creatCollectionRes.statusCode === 201) {
|
||||
return true;
|
||||
}
|
||||
|
@ -383,32 +377,32 @@ export class CosmosDB implements Component, Provisionable {
|
|||
return false;
|
||||
}
|
||||
|
||||
private getCosmosDbByNameFromCache(name: string) {
|
||||
private getCosmosDbByNameFromCache(name: string): {name: string}|undefined {
|
||||
return this.catchedCosmosDbList.find(item => item.name === name);
|
||||
}
|
||||
|
||||
private async getCosmosDbInResourceGroup() {
|
||||
private async getCosmosDbInResourceGroup(): Promise<vscode.QuickPickItem[]> {
|
||||
const resource = `/subscriptions/${
|
||||
AzureUtility.subscriptionId}/resourceGroups/${
|
||||
AzureUtility
|
||||
.resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts?api-version=2015-04-08`;
|
||||
AzureUtility.subscriptionId}/resourceGroups/${
|
||||
AzureUtility
|
||||
.resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts?api-version=2015-04-08`;
|
||||
const cosmosDbListRes = await AzureUtility.getRequest(resource) as
|
||||
{value: Array<{name: string, location: string}>};
|
||||
{value: Array<{name: string; location: string}>};
|
||||
const cosmosDbList: vscode.QuickPickItem[] =
|
||||
[{label: '$(plus) Create New Cosmos DB', description: ''}];
|
||||
[{ label: '$(plus) Create New Cosmos DB', description: '' }];
|
||||
for (const item of cosmosDbListRes.value) {
|
||||
cosmosDbList.push({label: item.name, description: item.location});
|
||||
cosmosDbList.push({ label: item.name, description: item.location });
|
||||
}
|
||||
this.catchedCosmosDbList = cosmosDbListRes.value;
|
||||
return cosmosDbList;
|
||||
}
|
||||
|
||||
private async getCosmosDbKey(name: string) {
|
||||
private async getCosmosDbKey(name: string): Promise<string> {
|
||||
const resource = `/subscriptions/${
|
||||
AzureUtility.subscriptionId}/resourceGroups/${
|
||||
AzureUtility
|
||||
.resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${
|
||||
name}/listKeys?api-version=2015-04-08`;
|
||||
AzureUtility.subscriptionId}/resourceGroups/${
|
||||
AzureUtility
|
||||
.resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${
|
||||
name}/listKeys?api-version=2015-04-08`;
|
||||
const cosmosDbKeyListRes =
|
||||
await AzureUtility.postRequest(resource) as {primaryMasterKey: string};
|
||||
return cosmosDbKeyListRes.primaryMasterKey;
|
||||
|
|
|
@ -3,40 +3,41 @@
|
|||
|
||||
import * as clipboardy from 'clipboardy';
|
||||
import * as fs from 'fs-plus';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import { Guid } from 'guid-typescript';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {BoardProvider} from '../boardProvider';
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {ConfigKey, OSPlatform} from '../constants';
|
||||
import {TelemetryContext} from '../telemetry';
|
||||
import { BoardProvider } from '../boardProvider';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { ConfigKey, OSPlatform } from '../constants';
|
||||
import { TelemetryContext } from '../telemetry';
|
||||
|
||||
import {ArduinoDeviceBase} from './ArduinoDeviceBase';
|
||||
import {DeviceType} from './Interfaces/Device';
|
||||
import {TemplateFileInfo} from './Interfaces/ProjectTemplate';
|
||||
import { ArduinoDeviceBase } from './ArduinoDeviceBase';
|
||||
import { DeviceType } from './Interfaces/Device';
|
||||
import { TemplateFileInfo } from './Interfaces/ProjectTemplate';
|
||||
import { Board } from './Interfaces/Board';
|
||||
|
||||
export class Esp32Device extends ArduinoDeviceBase {
|
||||
private templateFiles: TemplateFileInfo[] = [];
|
||||
private static _boardId = 'esp32';
|
||||
|
||||
private componentId: string;
|
||||
get id() {
|
||||
get id(): string {
|
||||
return this.componentId;
|
||||
}
|
||||
|
||||
static get boardId() {
|
||||
static get boardId(): string {
|
||||
return Esp32Device._boardId;
|
||||
}
|
||||
|
||||
get board() {
|
||||
get board(): Board | undefined {
|
||||
const boardProvider = new BoardProvider(this.boardFolderPath);
|
||||
const esp32 = boardProvider.find({id: Esp32Device._boardId});
|
||||
const esp32 = boardProvider.find({ id: Esp32Device._boardId });
|
||||
return esp32;
|
||||
}
|
||||
|
||||
get version() {
|
||||
get version(): string {
|
||||
const platform = os.platform();
|
||||
let packageRootPath = '';
|
||||
let version = '0.0.1';
|
||||
|
@ -45,7 +46,7 @@ export class Esp32Device extends ArduinoDeviceBase {
|
|||
const homeDir = os.homedir();
|
||||
const localAppData: string = path.join(homeDir, 'AppData', 'Local');
|
||||
packageRootPath = path.join(
|
||||
localAppData, 'Arduino15', 'packages', 'esp32', 'hardware', 'esp32');
|
||||
localAppData, 'Arduino15', 'packages', 'esp32', 'hardware', 'esp32');
|
||||
} else {
|
||||
packageRootPath = '~/Library/Arduino15/packages/esp32/hardware/esp32';
|
||||
}
|
||||
|
@ -63,11 +64,11 @@ export class Esp32Device extends ArduinoDeviceBase {
|
|||
name = 'Esp32Arduino';
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, devicePath: string,
|
||||
templateFiles?: TemplateFileInfo[]) {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, devicePath: string,
|
||||
templateFiles?: TemplateFileInfo[]) {
|
||||
super(
|
||||
context, devicePath, channel, telemetryContext, DeviceType.IoT_Button);
|
||||
context, devicePath, channel, telemetryContext, DeviceType.IoTButton);
|
||||
this.channel = channel;
|
||||
this.componentId = Guid.create().toString();
|
||||
if (templateFiles) {
|
||||
|
@ -112,7 +113,7 @@ export class Esp32Device extends ArduinoDeviceBase {
|
|||
|
||||
if (configSelection.detail === 'Config CRC') {
|
||||
const retValue: boolean =
|
||||
await this.generateCrc(this.extensionContext, this.channel);
|
||||
await this.generateCrc(this.channel);
|
||||
return retValue;
|
||||
} else if (configSelection.detail === 'Copy') {
|
||||
const deviceConnectionString =
|
||||
|
@ -120,7 +121,7 @@ export class Esp32Device extends ArduinoDeviceBase {
|
|||
|
||||
if (!deviceConnectionString) {
|
||||
throw new Error(
|
||||
'Unable to get the device connection string, please invoke the command of Azure Provision first.');
|
||||
'Unable to get the device connection string, please invoke the command of Azure Provision first.');
|
||||
}
|
||||
clipboardy.writeSync(deviceConnectionString);
|
||||
return true;
|
||||
|
|
|
@ -7,7 +7,7 @@ export interface CommandItem extends vscode.QuickPickItem {
|
|||
/**
|
||||
* Click action of the menu item
|
||||
*/
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
click?: (...args: any[]) => any;
|
||||
/**
|
||||
* Submenu of the menu item
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
export interface Compilable { compile(): Promise<boolean>; }
|
||||
export interface Compilable { compile(): Promise<boolean> }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
export interface Deployable { deploy(): Promise<boolean>; }
|
||||
export interface Deployable { deploy(): Promise<boolean> }
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import {ScaffoldType} from '../../constants';
|
||||
import { ScaffoldType } from '../../constants';
|
||||
|
||||
import {Compilable} from './Compilable';
|
||||
import {Component} from './Component';
|
||||
import {Uploadable} from './Uploadable';
|
||||
import { Compilable } from './Compilable';
|
||||
import { Component } from './Component';
|
||||
import { Uploadable } from './Uploadable';
|
||||
|
||||
export enum DeviceType {
|
||||
MXChip_AZ3166 = 1,
|
||||
IoT_Button = 2,
|
||||
MXChipAZ3166 = 1,
|
||||
IoTButton = 2,
|
||||
Esp32 = 3,
|
||||
Raspberry_Pi = 4
|
||||
RaspberryPi = 4
|
||||
}
|
||||
|
||||
export interface Device extends Component, Compilable, Uploadable {
|
||||
|
|
|
@ -16,7 +16,7 @@ export interface TemplateFileInfo {
|
|||
overwrite?: boolean;
|
||||
}
|
||||
|
||||
export interface TemplatesType { templates: ProjectTemplate[]; }
|
||||
export interface TemplatesType { templates: ProjectTemplate[] }
|
||||
export interface ProjectTemplate {
|
||||
platform: string;
|
||||
name: string;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
import {ScaffoldType} from '../../constants';
|
||||
import {ComponentInfo, DependencyConfig} from '../AzureComponentConfig';
|
||||
import { ScaffoldType } from '../../constants';
|
||||
import { ComponentInfo, DependencyConfig } from '../AzureComponentConfig';
|
||||
|
||||
export interface Provisionable {
|
||||
dependencies: DependencyConfig[];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import {QuickPickItem} from 'vscode';
|
||||
import { QuickPickItem } from 'vscode';
|
||||
|
||||
export interface PickWithData<T> extends QuickPickItem { data: T; }
|
||||
export interface PickWithData<T> extends QuickPickItem { data: T }
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
export interface Uploadable { upload(): Promise<boolean>; }
|
||||
export interface Uploadable { upload(): Promise<boolean> }
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
import * as fs from 'fs-plus';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import { Guid } from 'guid-typescript';
|
||||
import * as path from 'path';
|
||||
import * as request from 'request-promise';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {ConfigKey, ScaffoldType} from '../constants';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {generateTemplateFile} from '../utils';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { ConfigKey, ScaffoldType } from '../constants';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { generateTemplateFile } from '../utils';
|
||||
|
||||
import {ComponentType} from './Interfaces/Component';
|
||||
import {Device, DeviceType} from './Interfaces/Device';
|
||||
import {TemplateFileInfo} from './Interfaces/ProjectTemplate';
|
||||
import { ComponentType } from './Interfaces/Component';
|
||||
import { Device, DeviceType } from './Interfaces/Device';
|
||||
import { TemplateFileInfo } from './Interfaces/ProjectTemplate';
|
||||
|
||||
const constants = {
|
||||
timeout: 10000,
|
||||
|
@ -27,26 +27,22 @@ export class IoTButtonDevice implements Device {
|
|||
private deviceType: DeviceType;
|
||||
private componentType: ComponentType;
|
||||
private deviceFolder: string;
|
||||
private extensionContext: vscode.ExtensionContext;
|
||||
|
||||
private componentId: string;
|
||||
get id() {
|
||||
get id(): string {
|
||||
return this.componentId;
|
||||
}
|
||||
|
||||
private static _boardId = 'iotbutton';
|
||||
|
||||
static get boardId() {
|
||||
static get boardId(): string {
|
||||
return IoTButtonDevice._boardId;
|
||||
}
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, devicePath: string,
|
||||
private templateFilesInfo: TemplateFileInfo[] = []) {
|
||||
this.deviceType = DeviceType.IoT_Button;
|
||||
constructor(devicePath: string, private templateFilesInfo: TemplateFileInfo[] = []) {
|
||||
this.deviceType = DeviceType.IoTButton;
|
||||
this.componentType = ComponentType.Device;
|
||||
this.deviceFolder = devicePath;
|
||||
this.extensionContext = context;
|
||||
this.componentId = Guid.create().toString();
|
||||
}
|
||||
|
||||
|
@ -77,25 +73,25 @@ export class IoTButtonDevice implements Device {
|
|||
async create(): Promise<void> {
|
||||
const createTimeScaffoldType = ScaffoldType.Local;
|
||||
if (!await FileUtility.directoryExists(
|
||||
createTimeScaffoldType, this.deviceFolder)) {
|
||||
createTimeScaffoldType, this.deviceFolder)) {
|
||||
throw new Error(`Internal error: Couldn't find the template folder.`);
|
||||
}
|
||||
|
||||
for (const fileInfo of this.templateFilesInfo) {
|
||||
await generateTemplateFile(
|
||||
this.deviceFolder, createTimeScaffoldType, fileInfo);
|
||||
this.deviceFolder, createTimeScaffoldType, fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
async compile(): Promise<boolean> {
|
||||
vscode.window.showInformationMessage(
|
||||
'Congratulations! There is no device code to compile in this project.');
|
||||
'Congratulations! There is no device code to compile in this project.');
|
||||
return true;
|
||||
}
|
||||
|
||||
async upload(): Promise<boolean> {
|
||||
vscode.window.showInformationMessage(
|
||||
'Congratulations! There is no device code to upload in this project.');
|
||||
'Congratulations! There is no device code to upload in this project.');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -156,7 +152,7 @@ export class IoTButtonDevice implements Device {
|
|||
const res = await this.configHub();
|
||||
if (res) {
|
||||
vscode.window.showInformationMessage(
|
||||
'Config Azure IoT Hub successfully.');
|
||||
'Config Azure IoT Hub successfully.');
|
||||
}
|
||||
} catch (error) {
|
||||
vscode.window.showWarningMessage('Config IoT Hub failed.');
|
||||
|
@ -166,7 +162,7 @@ export class IoTButtonDevice implements Device {
|
|||
const res = await this.configNtp();
|
||||
if (res) {
|
||||
vscode.window.showInformationMessage(
|
||||
'Config time server successfully.');
|
||||
'Config time server successfully.');
|
||||
}
|
||||
} catch (error) {
|
||||
vscode.window.showWarningMessage('Config IoT Hub failed.');
|
||||
|
@ -176,14 +172,14 @@ export class IoTButtonDevice implements Device {
|
|||
const res = await this.configUserData();
|
||||
if (res) {
|
||||
vscode.window.showInformationMessage(
|
||||
'Config user data successfully.');
|
||||
'Config user data successfully.');
|
||||
}
|
||||
} catch (error) {
|
||||
vscode.window.showWarningMessage('Config user data failed.');
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const res = await this.configSaveAndShutdown();
|
||||
await this.configSaveAndShutdown();
|
||||
} catch (error) {
|
||||
// Ignore.
|
||||
// Because the button has been shutdown, we won't get any response for
|
||||
|
@ -197,9 +193,10 @@ export class IoTButtonDevice implements Device {
|
|||
return await this.configDeviceSettings();
|
||||
}
|
||||
|
||||
async setConfig(uri: string, data: {}) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async setConfig(uri: string, data: {}): Promise<any> {
|
||||
const option =
|
||||
{uri, method: 'POST', timeout: constants.timeout, form: data};
|
||||
{ uri, method: 'POST', timeout: constants.timeout, form: data };
|
||||
|
||||
const res = await request(option);
|
||||
|
||||
|
@ -210,7 +207,8 @@ export class IoTButtonDevice implements Device {
|
|||
return res;
|
||||
}
|
||||
|
||||
async configWifi() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async configWifi(): Promise<any> {
|
||||
const ssid = await vscode.window.showInputBox({
|
||||
prompt: `WiFi SSID`,
|
||||
ignoreFocusOut: true,
|
||||
|
@ -228,13 +226,13 @@ export class IoTButtonDevice implements Device {
|
|||
}
|
||||
|
||||
const password = await vscode.window.showInputBox(
|
||||
{prompt: `WiFi Password`, password: true, ignoreFocusOut: true});
|
||||
{ prompt: `WiFi Password`, password: true, ignoreFocusOut: true });
|
||||
|
||||
if (!password) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const data = {ssid, password};
|
||||
const data = { ssid, password };
|
||||
const uri = constants.accessEndpoint;
|
||||
|
||||
const res = await this.setConfig(uri, data);
|
||||
|
@ -242,7 +240,7 @@ export class IoTButtonDevice implements Device {
|
|||
return res;
|
||||
}
|
||||
|
||||
async configHub() {
|
||||
async configHub(): Promise<boolean> {
|
||||
let deviceConnectionString =
|
||||
ConfigHandler.get<string>(ConfigKey.iotHubDeviceConnectionString);
|
||||
|
||||
|
@ -318,7 +316,7 @@ export class IoTButtonDevice implements Device {
|
|||
(deviceConnectionString.indexOf('DeviceId') === -1) ||
|
||||
(deviceConnectionString.indexOf('SharedAccessKey') === -1)) {
|
||||
throw new Error(
|
||||
'The format of the IoT Hub Device connection string is invalid. Please provide a valid Device connection string.');
|
||||
'The format of the IoT Hub Device connection string is invalid. Please provide a valid Device connection string.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,7 +341,7 @@ export class IoTButtonDevice implements Device {
|
|||
const iotdevicename = iotdevicenameMatches[1];
|
||||
const iotdevicesecret = iotdevicesecretMatches[1];
|
||||
|
||||
const data = {iothub, iotdevicename, iotdevicesecret};
|
||||
const data = { iothub, iotdevicename, iotdevicesecret };
|
||||
const uri = constants.accessEndpoint;
|
||||
|
||||
const res = await this.setConfig(uri, data);
|
||||
|
@ -351,7 +349,8 @@ export class IoTButtonDevice implements Device {
|
|||
return res;
|
||||
}
|
||||
|
||||
async configUserData() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async configUserData(): Promise<any> {
|
||||
const deviceFolderPath = this.deviceFolder;
|
||||
|
||||
if (!fs.existsSync(deviceFolderPath)) {
|
||||
|
@ -373,7 +372,7 @@ export class IoTButtonDevice implements Device {
|
|||
userjson = {};
|
||||
}
|
||||
|
||||
const data = {userjson: JSON.stringify(userjson)};
|
||||
const data = { userjson: JSON.stringify(userjson) };
|
||||
const uri = constants.accessEndpoint;
|
||||
|
||||
const res = await this.setConfig(uri, data);
|
||||
|
@ -381,7 +380,8 @@ export class IoTButtonDevice implements Device {
|
|||
return res;
|
||||
}
|
||||
|
||||
async configNtp() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async configNtp(): Promise<any> {
|
||||
const timeserver = await vscode.window.showInputBox({
|
||||
value: 'pool.ntp.org',
|
||||
prompt: `Time Server`,
|
||||
|
@ -399,7 +399,7 @@ export class IoTButtonDevice implements Device {
|
|||
return false;
|
||||
}
|
||||
|
||||
const data = {timeserver};
|
||||
const data = { timeserver };
|
||||
const uri = constants.accessEndpoint;
|
||||
|
||||
const res = await this.setConfig(uri, data);
|
||||
|
@ -407,8 +407,9 @@ export class IoTButtonDevice implements Device {
|
|||
return res;
|
||||
}
|
||||
|
||||
async configSaveAndShutdown() {
|
||||
const data = {action: 'shutdown'};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async configSaveAndShutdown(): Promise<any> {
|
||||
const data = { action: 'shutdown' };
|
||||
const uri = constants.accessEndpoint;
|
||||
|
||||
const res = await this.setConfig(uri, data);
|
||||
|
@ -416,6 +417,8 @@ export class IoTButtonDevice implements Device {
|
|||
return res;
|
||||
}
|
||||
|
||||
async configDeviceEnvironment(
|
||||
deviceRootPath: string, scaffoldType: ScaffoldType): Promise<void> {}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async configDeviceEnvironment(_deviceRootPath: string, _scaffoldType: ScaffoldType): Promise<void> {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
|
@ -5,18 +5,18 @@ import * as fs from 'fs-plus';
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {CancelOperationError} from '../CancelOperationError';
|
||||
import {RemoteContainersCommands, VscodeCommands} from '../common/Commands';
|
||||
import {ConfigKey, EventNames, FileNames, ScaffoldType} from '../constants';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {TelemetryContext, TelemetryWorker} from '../telemetry';
|
||||
import {getProjectConfig, updateProjectHostTypeConfig} from '../utils';
|
||||
import { CancelOperationError } from '../CancelOperationError';
|
||||
import { RemoteContainersCommands, VscodeCommands } from '../common/Commands';
|
||||
import { ConfigKey, EventNames, FileNames, ScaffoldType } from '../constants';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { TelemetryContext, TelemetryWorker } from '../telemetry';
|
||||
import { getProjectConfig, updateProjectHostTypeConfig } from '../utils';
|
||||
|
||||
import {Component} from './Interfaces/Component';
|
||||
import {ProjectHostType} from './Interfaces/ProjectHostType';
|
||||
import {ProjectTemplateType, TemplateFileInfo} from './Interfaces/ProjectTemplate';
|
||||
import {IoTWorkbenchProjectBase, OpenScenario} from './IoTWorkbenchProjectBase';
|
||||
import {RemoteExtension} from './RemoteExtension';
|
||||
import { Component } from './Interfaces/Component';
|
||||
import { ProjectHostType } from './Interfaces/ProjectHostType';
|
||||
import { ProjectTemplateType, TemplateFileInfo } from './Interfaces/ProjectTemplate';
|
||||
import { IoTWorkbenchProjectBase, OpenScenario } from './IoTWorkbenchProjectBase';
|
||||
import { RemoteExtension } from './RemoteExtension';
|
||||
|
||||
const impor = require('impor')(__dirname);
|
||||
const raspberryPiDeviceModule =
|
||||
|
@ -24,13 +24,13 @@ const raspberryPiDeviceModule =
|
|||
|
||||
export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, rootFolderPath: string) {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, rootFolderPath: string) {
|
||||
super(context, channel, telemetryContext);
|
||||
this.projectHostType = ProjectHostType.Container;
|
||||
if (!rootFolderPath) {
|
||||
throw new Error(
|
||||
`Fail to construct iot workspace project: root folder path is empty.`);
|
||||
`Fail to construct iot workspace project: root folder path is empty.`);
|
||||
}
|
||||
this.projectRootPath = rootFolderPath;
|
||||
this.iotWorkbenchProjectFilePath =
|
||||
|
@ -43,7 +43,7 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
|
||||
// 1. Update iot workbench project file.
|
||||
await updateProjectHostTypeConfig(
|
||||
scaffoldType, this.iotWorkbenchProjectFilePath, this.projectHostType);
|
||||
scaffoldType, this.iotWorkbenchProjectFilePath, this.projectHostType);
|
||||
|
||||
// 2. Send load project event telemetry only if the IoT project is loaded
|
||||
// when VS Code opens.
|
||||
|
@ -57,14 +57,12 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
const boardId = projectConfigJson[`${ConfigKey.boardId}`];
|
||||
if (!boardId) {
|
||||
throw new Error(
|
||||
`Internal Error: Fail to get board id from configuration.`);
|
||||
`Internal Error: Fail to get board id from configuration.`);
|
||||
}
|
||||
await this.initDevice(boardId, scaffoldType);
|
||||
}
|
||||
|
||||
async create(
|
||||
templateFilesInfo: TemplateFileInfo[], projectType: ProjectTemplateType,
|
||||
boardId: string, openInNewWindow: boolean): Promise<void> {
|
||||
async create(templateFilesInfo: TemplateFileInfo[], _projectType: ProjectTemplateType, boardId: string, openInNewWindow: boolean): Promise<void> {
|
||||
// Can only create project locally
|
||||
await RemoteExtension.checkRemoteExtension();
|
||||
|
||||
|
@ -72,18 +70,18 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
|
||||
// Create project root path
|
||||
if (!await FileUtility.directoryExists(
|
||||
createTimeScaffoldType, this.projectRootPath)) {
|
||||
createTimeScaffoldType, this.projectRootPath)) {
|
||||
await FileUtility.mkdirRecursively(
|
||||
createTimeScaffoldType, this.projectRootPath);
|
||||
createTimeScaffoldType, this.projectRootPath);
|
||||
}
|
||||
|
||||
// Update iot workbench project file
|
||||
await updateProjectHostTypeConfig(
|
||||
createTimeScaffoldType, this.iotWorkbenchProjectFilePath,
|
||||
this.projectHostType);
|
||||
createTimeScaffoldType, this.iotWorkbenchProjectFilePath,
|
||||
this.projectHostType);
|
||||
|
||||
const projectConfig = await getProjectConfig(
|
||||
createTimeScaffoldType, this.iotWorkbenchProjectFilePath);
|
||||
createTimeScaffoldType, this.iotWorkbenchProjectFilePath);
|
||||
|
||||
// Step 1: Create device
|
||||
await this.initDevice(boardId, createTimeScaffoldType, templateFilesInfo);
|
||||
|
@ -92,18 +90,18 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
// Update workspace config to workspace config file
|
||||
if (!this.iotWorkbenchProjectFilePath) {
|
||||
throw new Error(
|
||||
`Workspace config file path is empty. Please initialize the project first.`);
|
||||
`Workspace config file path is empty. Please initialize the project first.`);
|
||||
}
|
||||
await FileUtility.writeJsonFile(
|
||||
createTimeScaffoldType, this.iotWorkbenchProjectFilePath,
|
||||
projectConfig);
|
||||
createTimeScaffoldType, this.iotWorkbenchProjectFilePath,
|
||||
projectConfig);
|
||||
|
||||
// Check components prerequisites
|
||||
this.componentList.forEach(async item => {
|
||||
const res = await item.checkPrerequisites();
|
||||
if (!res) {
|
||||
throw new Error(
|
||||
`Failed to create component because prerequisite is not met.`);
|
||||
`Failed to create component because prerequisite is not met.`);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -119,7 +117,7 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
|
||||
// Open project
|
||||
await this.openProject(
|
||||
createTimeScaffoldType, openInNewWindow, OpenScenario.createNewProject);
|
||||
createTimeScaffoldType, openInNewWindow, OpenScenario.createNewProject);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,8 +125,8 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
* If yes, open project in container. If not, stay local.
|
||||
*/
|
||||
async openProject(
|
||||
scaffoldType: ScaffoldType, openInNewWindow: boolean,
|
||||
openScenario: OpenScenario): Promise<void> {
|
||||
scaffoldType: ScaffoldType, openInNewWindow: boolean,
|
||||
openScenario: OpenScenario): Promise<void> {
|
||||
this.validateProjectRootPath(scaffoldType);
|
||||
|
||||
// 1. Ask to customize
|
||||
|
@ -144,8 +142,8 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
const telemetryWorker =
|
||||
TelemetryWorker.getInstance(this.extensionContext);
|
||||
const eventNames = openScenario === OpenScenario.createNewProject ?
|
||||
EventNames.createNewProjectEvent :
|
||||
EventNames.configProjectEnvironmentEvent;
|
||||
EventNames.createNewProjectEvent :
|
||||
EventNames.configProjectEnvironmentEvent;
|
||||
telemetryWorker.sendEvent(eventNames, this.telemetryContext);
|
||||
} catch {
|
||||
// If sending telemetry failed, skip the error to avoid blocking user.
|
||||
|
@ -157,22 +155,22 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
await this.openFolderInContainer(this.projectRootPath);
|
||||
} else {
|
||||
await vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpenFolder,
|
||||
vscode.Uri.file(this.projectRootPath), openInNewWindow);
|
||||
VscodeCommands.VscodeOpenFolder,
|
||||
vscode.Uri.file(this.projectRootPath), openInNewWindow);
|
||||
// TODO: open install_packages.sh bash script.
|
||||
}
|
||||
}
|
||||
|
||||
private async openFolderInContainer(folderPath: string) {
|
||||
private async openFolderInContainer(folderPath: string): Promise<void> {
|
||||
if (!await FileUtility.directoryExists(ScaffoldType.Local, folderPath)) {
|
||||
throw new Error(
|
||||
`Fail to open folder in container: ${folderPath} does not exist.`);
|
||||
`Fail to open folder in container: ${folderPath} does not exist.`);
|
||||
}
|
||||
|
||||
await RemoteExtension.checkRemoteExtension();
|
||||
|
||||
await vscode.commands.executeCommand(
|
||||
RemoteContainersCommands.OpenFolder, vscode.Uri.file(folderPath));
|
||||
RemoteContainersCommands.OpenFolder, vscode.Uri.file(folderPath));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,15 +181,15 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
* @param templateFilesInfo template files info to scaffold files for device
|
||||
*/
|
||||
private async initDevice(
|
||||
boardId: string, scaffoldType: ScaffoldType,
|
||||
templateFilesInfo?: TemplateFileInfo[]): Promise<void> {
|
||||
boardId: string, scaffoldType: ScaffoldType,
|
||||
templateFilesInfo?: TemplateFileInfo[]): Promise<void> {
|
||||
this.validateProjectRootPath(scaffoldType);
|
||||
|
||||
let device: Component;
|
||||
if (boardId === raspberryPiDeviceModule.RaspberryPiDevice.boardId) {
|
||||
device = new raspberryPiDeviceModule.RaspberryPiDevice(
|
||||
this.extensionContext, this.projectRootPath, this.channel,
|
||||
this.telemetryContext, templateFilesInfo);
|
||||
this.extensionContext, this.projectRootPath, this.channel,
|
||||
this.telemetryContext, templateFilesInfo);
|
||||
} else {
|
||||
throw new Error(`The board ${boardId} is not supported.`);
|
||||
}
|
||||
|
@ -209,14 +207,14 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
private async askToOpenInContainer(): Promise<boolean> {
|
||||
const openInContainerOption: vscode.QuickPickItem[] = [];
|
||||
openInContainerOption.push(
|
||||
{
|
||||
label: `Yes`,
|
||||
detail: 'I want to work on this project in container now.'
|
||||
},
|
||||
{
|
||||
label: `No`,
|
||||
detail: 'I need to customize my container first locally.'
|
||||
});
|
||||
{
|
||||
label: `Yes`,
|
||||
detail: 'I want to work on this project in container now.'
|
||||
},
|
||||
{
|
||||
label: `No`,
|
||||
detail: 'I need to customize my container first locally.'
|
||||
});
|
||||
|
||||
const openInContainerSelection =
|
||||
await vscode.window.showQuickPick(openInContainerOption, {
|
||||
|
@ -226,7 +224,7 @@ export class IoTContainerizedProject extends IoTWorkbenchProjectBase {
|
|||
|
||||
if (!openInContainerSelection) {
|
||||
throw new CancelOperationError(
|
||||
`Ask to customize development environment selection cancelled.`);
|
||||
`Ask to customize development environment selection cancelled.`);
|
||||
}
|
||||
|
||||
return openInContainerSelection.label === 'Yes';
|
||||
|
|
|
@ -2,20 +2,20 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
import * as fs from 'fs-plus';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import { Guid } from 'guid-typescript';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {AzureComponentsStorage, ConfigKey, ScaffoldType} from '../constants';
|
||||
import {channelPrintJsonObject, channelShowAndAppendLine} from '../utils';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { AzureComponentsStorage, ConfigKey, ScaffoldType } from '../constants';
|
||||
import { channelPrintJsonObject, channelShowAndAppendLine } from '../utils';
|
||||
|
||||
import {getExtension} from './Apis';
|
||||
import {AzureComponentConfig, AzureConfigFileHandler, AzureConfigs, ComponentInfo, DependencyConfig} from './AzureComponentConfig';
|
||||
import {AzureUtility} from './AzureUtility';
|
||||
import {ExtensionName} from './Interfaces/Api';
|
||||
import {Component, ComponentType} from './Interfaces/Component';
|
||||
import {Provisionable} from './Interfaces/Provisionable';
|
||||
import { getExtension } from './Apis';
|
||||
import { AzureComponentConfig, AzureConfigFileHandler, AzureConfigs, ComponentInfo, DependencyConfig } from './AzureComponentConfig';
|
||||
import { AzureUtility } from './AzureUtility';
|
||||
import { ExtensionName } from './Interfaces/Api';
|
||||
import { Component, ComponentType } from './Interfaces/Component';
|
||||
import { Provisionable } from './Interfaces/Provisionable';
|
||||
|
||||
export class IoTHub implements Component, Provisionable {
|
||||
dependencies: DependencyConfig[] = [];
|
||||
|
@ -24,7 +24,7 @@ export class IoTHub implements Component, Provisionable {
|
|||
private projectRootPath: string;
|
||||
private componentId: string;
|
||||
private azureConfigFileHandler: AzureConfigFileHandler;
|
||||
get id() {
|
||||
get id(): string {
|
||||
return this.componentId;
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,8 @@ export class IoTHub implements Component, Provisionable {
|
|||
|
||||
async load(): Promise<boolean> {
|
||||
const azureConfigFilePath = path.join(
|
||||
this.projectRootPath, AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
this.projectRootPath, AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
|
||||
if (!fs.existsSync(azureConfigFilePath)) {
|
||||
return false;
|
||||
|
@ -60,7 +60,7 @@ export class IoTHub implements Component, Provisionable {
|
|||
try {
|
||||
azureConfigs = JSON.parse(fs.readFileSync(azureConfigFilePath, 'utf8'));
|
||||
const iotHubConfig = azureConfigs.componentConfigs.find(
|
||||
config => config.type === this.componentType);
|
||||
config => config.type === this.componentType);
|
||||
if (iotHubConfig) {
|
||||
this.componentId = iotHubConfig.id;
|
||||
this.dependencies = iotHubConfig.dependencies;
|
||||
|
@ -91,8 +91,8 @@ export class IoTHub implements Component, Provisionable {
|
|||
}
|
||||
];
|
||||
const selection = await vscode.window.showQuickPick(
|
||||
provisionIothubSelection,
|
||||
{ignoreFocusOut: true, placeHolder: 'Provision IoT Hub'});
|
||||
provisionIothubSelection,
|
||||
{ ignoreFocusOut: true, placeHolder: 'Provision IoT Hub' });
|
||||
|
||||
if (!selection) {
|
||||
return false;
|
||||
|
@ -101,7 +101,7 @@ export class IoTHub implements Component, Provisionable {
|
|||
const toolkit = getExtension(ExtensionName.Toolkit);
|
||||
if (!toolkit) {
|
||||
throw new Error(
|
||||
'Azure IoT Hub Toolkit is not installed. Please install it from Marketplace.');
|
||||
'Azure IoT Hub Toolkit is not installed. Please install it from Marketplace.');
|
||||
}
|
||||
|
||||
let iothub = null;
|
||||
|
@ -109,20 +109,20 @@ export class IoTHub implements Component, Provisionable {
|
|||
const resourceGroup = AzureUtility.resourceGroup;
|
||||
|
||||
switch (selection.detail) {
|
||||
case 'select':
|
||||
iothub = await toolkit.azureIoTExplorer.selectIoTHub(
|
||||
this.channel, subscriptionId);
|
||||
break;
|
||||
case 'create':
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(this.channel, 'Creating new IoT Hub...');
|
||||
}
|
||||
case 'select':
|
||||
iothub = await toolkit.azureIoTExplorer.selectIoTHub(
|
||||
this.channel, subscriptionId);
|
||||
break;
|
||||
case 'create':
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(this.channel, 'Creating new IoT Hub...');
|
||||
}
|
||||
|
||||
iothub = await toolkit.azureIoTExplorer.createIoTHub(
|
||||
this.channel, subscriptionId, resourceGroup);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
iothub = await toolkit.azureIoTExplorer.createIoTHub(
|
||||
this.channel, subscriptionId, resourceGroup);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (iothub && iothub.iotHubConnectionString) {
|
||||
|
@ -134,24 +134,24 @@ export class IoTHub implements Component, Provisionable {
|
|||
iothub.iotHubConnectionString.match(/SharedAccessKey=([^;]*)/);
|
||||
if (!sharedAccessKeyMatches || sharedAccessKeyMatches.length < 2) {
|
||||
throw new Error(
|
||||
'Cannot parse shared access key from IoT Hub connection string. Please retry Azure Provision.');
|
||||
'Cannot parse shared access key from IoT Hub connection string. Please retry Azure Provision.');
|
||||
}
|
||||
|
||||
const sharedAccessKey = sharedAccessKeyMatches[1];
|
||||
|
||||
const eventHubConnectionString = `Endpoint=${
|
||||
iothub.properties.eventHubEndpoints.events
|
||||
.endpoint};SharedAccessKeyName=iothubowner;SharedAccessKey=${
|
||||
sharedAccessKey}`;
|
||||
iothub.properties.eventHubEndpoints.events
|
||||
.endpoint};SharedAccessKeyName=iothubowner;SharedAccessKey=${
|
||||
sharedAccessKey}`;
|
||||
const eventHubConnectionPath =
|
||||
iothub.properties.eventHubEndpoints.events.path;
|
||||
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.iotHubConnectionString, iothub.iotHubConnectionString);
|
||||
ConfigKey.iotHubConnectionString, iothub.iotHubConnectionString);
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.eventHubConnectionString, eventHubConnectionString);
|
||||
ConfigKey.eventHubConnectionString, eventHubConnectionString);
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.eventHubConnectionPath, eventHubConnectionPath);
|
||||
ConfigKey.eventHubConnectionPath, eventHubConnectionPath);
|
||||
|
||||
const scaffoldType = ScaffoldType.Workspace;
|
||||
await this.updateConfigSettings(scaffoldType, {
|
||||
|
@ -170,7 +170,7 @@ export class IoTHub implements Component, Provisionable {
|
|||
return false;
|
||||
} else {
|
||||
throw new Error(
|
||||
'IoT Hub provision failed. Please check output window for detail.');
|
||||
'IoT Hub provision failed. Please check output window for detail.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ export class IoTHub implements Component, Provisionable {
|
|||
return;
|
||||
}
|
||||
await this.azureConfigFileHandler.updateComponent(
|
||||
type, iotHubComponentIndex, componentInfo);
|
||||
type, iotHubComponentIndex, componentInfo);
|
||||
} else {
|
||||
const newIoTHubConfig: AzureComponentConfig = {
|
||||
id: this.id,
|
||||
|
|
|
@ -2,107 +2,37 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
import * as iothub from 'azure-iothub';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import { Guid } from 'guid-typescript';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {ConfigKey, ScaffoldType} from '../constants';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { ConfigKey, ScaffoldType } from '../constants';
|
||||
|
||||
import {getExtension} from './Apis';
|
||||
import {ComponentInfo, DependencyConfig} from './AzureComponentConfig';
|
||||
import {ExtensionName} from './Interfaces/Api';
|
||||
import {Component, ComponentType} from './Interfaces/Component';
|
||||
import {Provisionable} from './Interfaces/Provisionable';
|
||||
import { getExtension } from './Apis';
|
||||
import { ComponentInfo, DependencyConfig } from './AzureComponentConfig';
|
||||
import { ExtensionName } from './Interfaces/Api';
|
||||
import { Component, ComponentType } from './Interfaces/Component';
|
||||
import { Provisionable } from './Interfaces/Provisionable';
|
||||
|
||||
export class IoTHubDevice implements Component, Provisionable {
|
||||
private componentType: ComponentType;
|
||||
private channel: vscode.OutputChannel;
|
||||
private componentId: string;
|
||||
get id() {
|
||||
return this.componentId;
|
||||
}
|
||||
|
||||
dependencies: DependencyConfig[] = [];
|
||||
|
||||
constructor(channel: vscode.OutputChannel) {
|
||||
this.componentType = ComponentType.IoTHubDevice;
|
||||
this.channel = channel;
|
||||
this.componentId = Guid.create().toString();
|
||||
}
|
||||
|
||||
name = 'IoT Hub Device';
|
||||
|
||||
getComponentType(): ComponentType {
|
||||
return this.componentType;
|
||||
}
|
||||
|
||||
async checkPrerequisites(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
async load(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
async create(): Promise<void> {}
|
||||
|
||||
async provision(): Promise<boolean> {
|
||||
const iotHubConnectionString =
|
||||
ConfigHandler.get<string>(ConfigKey.iotHubConnectionString);
|
||||
if (!iotHubConnectionString) {
|
||||
throw new Error(
|
||||
'Unable to find IoT Hub connection in the project. Please retry Azure Provision.');
|
||||
}
|
||||
|
||||
const selection = await vscode.window.showQuickPick(
|
||||
getProvisionIothubDeviceSelection(iotHubConnectionString),
|
||||
{ignoreFocusOut: true, placeHolder: 'Provision IoTHub Device'});
|
||||
|
||||
if (!selection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const toolkit = getExtension(ExtensionName.Toolkit);
|
||||
if (!toolkit) {
|
||||
throw new Error(
|
||||
'Azure IoT Hub Toolkit is not installed. Please install it from Marketplace.');
|
||||
}
|
||||
|
||||
let device = null;
|
||||
switch (selection.detail) {
|
||||
case 'select':
|
||||
device = await toolkit.azureIoTExplorer.getDevice(
|
||||
null, iotHubConnectionString, this.channel);
|
||||
if (!device) {
|
||||
return false;
|
||||
} else {
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.iotHubDeviceConnectionString, device.connectionString);
|
||||
async function getDeviceNumber(iotHubConnectionString: string): Promise<number> {
|
||||
return new Promise(
|
||||
(resolve: (value: number) => void, reject: (error: Error) => void) => {
|
||||
const registry: iothub.Registry =
|
||||
iothub.Registry.fromConnectionString(iotHubConnectionString);
|
||||
registry.list((err, list) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'create':
|
||||
device = await toolkit.azureIoTExplorer.createDevice(
|
||||
false, iotHubConnectionString, this.channel);
|
||||
if (!device) {
|
||||
return false;
|
||||
if (!list) {
|
||||
return resolve(0);
|
||||
} else {
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.iotHubDeviceConnectionString, device.connectionString);
|
||||
return resolve(list.length);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
updateConfigSettings(type: ScaffoldType, componentInfo?: ComponentInfo):
|
||||
void {}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function getProvisionIothubDeviceSelection(
|
||||
iotHubConnectionString: string) {
|
||||
async function getProvisionIothubDeviceSelection(iotHubConnectionString: string): Promise<vscode.QuickPickItem[]> {
|
||||
let provisionIothubDeviceSelection: vscode.QuickPickItem[];
|
||||
|
||||
const deviceNumber = await getDeviceNumber(iotHubConnectionString);
|
||||
|
@ -129,20 +59,93 @@ async function getProvisionIothubDeviceSelection(
|
|||
return provisionIothubDeviceSelection;
|
||||
}
|
||||
|
||||
async function getDeviceNumber(iotHubConnectionString: string) {
|
||||
return new Promise(
|
||||
(resolve: (value: number) => void, reject: (error: Error) => void) => {
|
||||
const registry: iothub.Registry =
|
||||
iothub.Registry.fromConnectionString(iotHubConnectionString);
|
||||
registry.list((err, list) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
if (!list) {
|
||||
return resolve(0);
|
||||
} else {
|
||||
return resolve(list.length);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
export class IoTHubDevice implements Component, Provisionable {
|
||||
private componentType: ComponentType;
|
||||
private channel: vscode.OutputChannel;
|
||||
private componentId: string;
|
||||
get id(): string {
|
||||
return this.componentId;
|
||||
}
|
||||
|
||||
dependencies: DependencyConfig[] = [];
|
||||
|
||||
constructor(channel: vscode.OutputChannel) {
|
||||
this.componentType = ComponentType.IoTHubDevice;
|
||||
this.channel = channel;
|
||||
this.componentId = Guid.create().toString();
|
||||
}
|
||||
|
||||
name = 'IoT Hub Device';
|
||||
|
||||
getComponentType(): ComponentType {
|
||||
return this.componentType;
|
||||
}
|
||||
|
||||
async checkPrerequisites(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
async load(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
async create(): Promise<void> {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
async provision(): Promise<boolean> {
|
||||
const iotHubConnectionString =
|
||||
ConfigHandler.get<string>(ConfigKey.iotHubConnectionString);
|
||||
if (!iotHubConnectionString) {
|
||||
throw new Error(
|
||||
'Unable to find IoT Hub connection in the project. Please retry Azure Provision.');
|
||||
}
|
||||
|
||||
const selection = await vscode.window.showQuickPick(
|
||||
getProvisionIothubDeviceSelection(iotHubConnectionString),
|
||||
{ ignoreFocusOut: true, placeHolder: 'Provision IoTHub Device' });
|
||||
|
||||
if (!selection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const toolkit = getExtension(ExtensionName.Toolkit);
|
||||
if (!toolkit) {
|
||||
throw new Error(
|
||||
'Azure IoT Hub Toolkit is not installed. Please install it from Marketplace.');
|
||||
}
|
||||
|
||||
let device = null;
|
||||
switch (selection.detail) {
|
||||
case 'select':
|
||||
device = await toolkit.azureIoTExplorer.getDevice(
|
||||
null, iotHubConnectionString, this.channel);
|
||||
if (!device) {
|
||||
return false;
|
||||
} else {
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.iotHubDeviceConnectionString, device.connectionString);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'create':
|
||||
device = await toolkit.azureIoTExplorer.createDevice(
|
||||
false, iotHubConnectionString, this.channel);
|
||||
if (!device) {
|
||||
return false;
|
||||
} else {
|
||||
await ConfigHandler.update(
|
||||
ConfigKey.iotHubDeviceConnectionString, device.connectionString);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
updateConfigSettings(_type: ScaffoldType, _componentInfo?: ComponentInfo): void {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,21 +4,21 @@
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {CancelOperationError} from '../CancelOperationError';
|
||||
import {ConfigKey, EventNames, FileNames, ScaffoldType} from '../constants';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {TelemetryContext, TelemetryWorker} from '../telemetry';
|
||||
import { CancelOperationError } from '../CancelOperationError';
|
||||
import { ConfigKey, EventNames, FileNames, ScaffoldType } from '../constants';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { TelemetryContext, TelemetryWorker } from '../telemetry';
|
||||
import * as utils from '../utils';
|
||||
|
||||
import {checkAzureLogin} from './Apis';
|
||||
import {Compilable} from './Interfaces/Compilable';
|
||||
import {Component, ComponentType} from './Interfaces/Component';
|
||||
import {Deployable} from './Interfaces/Deployable';
|
||||
import {Device} from './Interfaces/Device';
|
||||
import {ProjectHostType} from './Interfaces/ProjectHostType';
|
||||
import {ProjectTemplateType, TemplateFileInfo} from './Interfaces/ProjectTemplate';
|
||||
import {Provisionable} from './Interfaces/Provisionable';
|
||||
import {Uploadable} from './Interfaces/Uploadable';
|
||||
import { checkAzureLogin } from './Apis';
|
||||
import { Compilable } from './Interfaces/Compilable';
|
||||
import { Component, ComponentType } from './Interfaces/Component';
|
||||
import { Deployable } from './Interfaces/Deployable';
|
||||
import { Device } from './Interfaces/Device';
|
||||
import { ProjectHostType } from './Interfaces/ProjectHostType';
|
||||
import { ProjectTemplateType, TemplateFileInfo } from './Interfaces/ProjectTemplate';
|
||||
import { Provisionable } from './Interfaces/Provisionable';
|
||||
import { Uploadable } from './Interfaces/Uploadable';
|
||||
|
||||
const impor = require('impor')(__dirname);
|
||||
const azureUtilityModule =
|
||||
|
@ -45,8 +45,8 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
* @param projectFileRootPath
|
||||
*/
|
||||
static async getProjectType(
|
||||
scaffoldType: ScaffoldType,
|
||||
projectFileRootPath: string|undefined): Promise<ProjectHostType> {
|
||||
scaffoldType: ScaffoldType,
|
||||
projectFileRootPath: string|undefined): Promise<ProjectHostType> {
|
||||
if (!projectFileRootPath) {
|
||||
return ProjectHostType.Unknown;
|
||||
}
|
||||
|
@ -57,14 +57,14 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
}
|
||||
const iotWorkbenchProjectFileString =
|
||||
(await FileUtility.readFile(
|
||||
scaffoldType, iotWorkbenchProjectFile, 'utf8') as string)
|
||||
.trim();
|
||||
scaffoldType, iotWorkbenchProjectFile, 'utf8') as string)
|
||||
.trim();
|
||||
if (iotWorkbenchProjectFileString) {
|
||||
const projectConfig = JSON.parse(iotWorkbenchProjectFileString);
|
||||
if (projectConfig &&
|
||||
projectConfig[`${ConfigKey.projectHostType}`] !== undefined) {
|
||||
const projectHostType: ProjectHostType = utils.getEnumKeyByEnumValue(
|
||||
ProjectHostType, projectConfig[`${ConfigKey.projectHostType}`]);
|
||||
ProjectHostType, projectConfig[`${ConfigKey.projectHostType}`]);
|
||||
return projectHostType;
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
const devcontainerFolderPath =
|
||||
path.join(projectFileRootPath, FileNames.devcontainerFolderName);
|
||||
if (await FileUtility.directoryExists(
|
||||
scaffoldType, devcontainerFolderPath)) {
|
||||
scaffoldType, devcontainerFolderPath)) {
|
||||
return ProjectHostType.Container;
|
||||
} else {
|
||||
return ProjectHostType.Workspace;
|
||||
|
@ -97,8 +97,8 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
}
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
this.componentList = [];
|
||||
this.extensionContext = context;
|
||||
this.channel = channel;
|
||||
|
@ -123,7 +123,7 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
const res = await item.compile();
|
||||
if (!res) {
|
||||
vscode.window.showErrorMessage(
|
||||
'Unable to compile the device code, please check output window for detail.');
|
||||
'Unable to compile the device code, please check output window for detail.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
const res = await item.upload();
|
||||
if (!res) {
|
||||
vscode.window.showErrorMessage(
|
||||
'Unable to upload the sketch, please check output window for detail.');
|
||||
'Unable to upload the sketch, please check output window for detail.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
if (provisionItemList.length === 0) {
|
||||
// nothing to provision:
|
||||
vscode.window.showInformationMessage(
|
||||
'Congratulations! There is no Azure service to provision in this project.');
|
||||
'Congratulations! There is no Azure service to provision in this project.');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -194,12 +194,12 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
}
|
||||
}
|
||||
const selection = await vscode.window.showQuickPick(
|
||||
[{
|
||||
label: _provisionItemList.join(' - '),
|
||||
description: '',
|
||||
detail: 'Click to continue'
|
||||
}],
|
||||
{ignoreFocusOut: true, placeHolder: 'Provision process'});
|
||||
[{
|
||||
label: _provisionItemList.join(' - '),
|
||||
description: '',
|
||||
detail: 'Click to continue'
|
||||
}],
|
||||
{ ignoreFocusOut: true, placeHolder: 'Provision process' });
|
||||
|
||||
if (!selection) {
|
||||
return false;
|
||||
|
@ -214,7 +214,7 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
return true;
|
||||
}
|
||||
|
||||
async deploy() {
|
||||
async deploy(): Promise<void> {
|
||||
let azureLoggedIn = false;
|
||||
|
||||
const deployItemList: string[] = [];
|
||||
|
@ -231,7 +231,7 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
|
||||
if (deployItemList && deployItemList.length <= 0) {
|
||||
await vscode.window.showInformationMessage(
|
||||
'Congratulations! The project does not contain any Azure components to be deployed.');
|
||||
'Congratulations! The project does not contain any Azure components to be deployed.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -250,12 +250,12 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
}
|
||||
}
|
||||
const selection = await vscode.window.showQuickPick(
|
||||
[{
|
||||
label: _deployItemList.join(' - '),
|
||||
description: '',
|
||||
detail: 'Click to continue'
|
||||
}],
|
||||
{ignoreFocusOut: true, placeHolder: 'Deploy process'});
|
||||
[{
|
||||
label: _deployItemList.join(' - '),
|
||||
description: '',
|
||||
detail: 'Click to continue'
|
||||
}],
|
||||
{ ignoreFocusOut: true, placeHolder: 'Deploy process' });
|
||||
|
||||
if (!selection) {
|
||||
throw new CancelOperationError(`Component deployment cancelled.`);
|
||||
|
@ -276,7 +276,7 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
* template files.
|
||||
*/
|
||||
async configureProjectEnvironmentCore(
|
||||
deviceRootPath: string, scaffoldType: ScaffoldType): Promise<void> {
|
||||
deviceRootPath: string, scaffoldType: ScaffoldType): Promise<void> {
|
||||
for (const component of this.componentList) {
|
||||
if (component.getComponentType() === ComponentType.Device) {
|
||||
const device = component as Device;
|
||||
|
@ -302,11 +302,11 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
/**
|
||||
* Send telemetry when the IoT project is load when VS Code opens
|
||||
*/
|
||||
sendLoadEventTelemetry(context: vscode.ExtensionContext) {
|
||||
sendLoadEventTelemetry(context: vscode.ExtensionContext): void {
|
||||
const telemetryWorker = TelemetryWorker.getInstance(context);
|
||||
try {
|
||||
telemetryWorker.sendEvent(
|
||||
EventNames.projectLoadEvent, this.telemetryContext);
|
||||
EventNames.projectLoadEvent, this.telemetryContext);
|
||||
} catch {
|
||||
// If sending telemetry failed, skip the error to avoid blocking user.
|
||||
}
|
||||
|
@ -318,9 +318,9 @@ export abstract class IoTWorkbenchProjectBase {
|
|||
*/
|
||||
async validateProjectRootPath(scaffoldType: ScaffoldType): Promise<void> {
|
||||
if (!await FileUtility.directoryExists(
|
||||
scaffoldType, this.projectRootPath)) {
|
||||
scaffoldType, this.projectRootPath)) {
|
||||
throw new Error(`Project root path ${
|
||||
this.projectRootPath} does not exist. Please initialize the project first.`);
|
||||
this.projectRootPath} does not exist. Please initialize the project first.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,19 +5,19 @@ import * as fs from 'fs-plus';
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {IoTCubeCommands} from '../common/Commands';
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {ConfigKey, EventNames, FileNames, ScaffoldType} from '../constants';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {TelemetryContext, TelemetryWorker} from '../telemetry';
|
||||
import {getWorkspaceFile, updateProjectHostTypeConfig} from '../utils';
|
||||
import { IoTCubeCommands } from '../common/Commands';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { ConfigKey, EventNames, FileNames, ScaffoldType } from '../constants';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { TelemetryContext, TelemetryWorker } from '../telemetry';
|
||||
import { getWorkspaceFile, updateProjectHostTypeConfig } from '../utils';
|
||||
|
||||
import {AzureComponentConfig, Dependency} from './AzureComponentConfig';
|
||||
import {Component, ComponentType} from './Interfaces/Component';
|
||||
import {ProjectHostType} from './Interfaces/ProjectHostType';
|
||||
import {ProjectTemplateType, TemplateFileInfo} from './Interfaces/ProjectTemplate';
|
||||
import {Workspace} from './Interfaces/Workspace';
|
||||
import {IoTWorkbenchProjectBase, OpenScenario} from './IoTWorkbenchProjectBase';
|
||||
import { AzureComponentConfig, Dependency } from './AzureComponentConfig';
|
||||
import { Component, ComponentType } from './Interfaces/Component';
|
||||
import { ProjectHostType } from './Interfaces/ProjectHostType';
|
||||
import { ProjectTemplateType, TemplateFileInfo } from './Interfaces/ProjectTemplate';
|
||||
import { Workspace } from './Interfaces/Workspace';
|
||||
import { IoTWorkbenchProjectBase, OpenScenario } from './IoTWorkbenchProjectBase';
|
||||
|
||||
const impor = require('impor')(__dirname);
|
||||
const az3166DeviceModule =
|
||||
|
@ -48,13 +48,13 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
private workspaceConfigFilePath = '';
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, rootFolderPath: string) {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, rootFolderPath: string) {
|
||||
super(context, channel, telemetryContext);
|
||||
this.projectHostType = ProjectHostType.Workspace;
|
||||
if (!rootFolderPath) {
|
||||
throw new Error(
|
||||
`Fail to construct iot workspace project: root folder path is empty.`);
|
||||
`Fail to construct iot workspace project: root folder path is empty.`);
|
||||
}
|
||||
this.projectRootPath = rootFolderPath;
|
||||
this.telemetryContext.properties.projectHostType = this.projectHostType;
|
||||
|
@ -67,19 +67,19 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
const devicePath = ConfigHandler.get<string>(ConfigKey.devicePath);
|
||||
if (!devicePath) {
|
||||
throw new Error(
|
||||
`Internal Error: Fail to get device path from configuration.`);
|
||||
`Internal Error: Fail to get device path from configuration.`);
|
||||
}
|
||||
this.deviceRootPath = path.join(this.projectRootPath, devicePath);
|
||||
if (!await FileUtility.directoryExists(scaffoldType, this.deviceRootPath)) {
|
||||
throw new Error(
|
||||
`Device root path ${this.deviceRootPath} does not exist.`);
|
||||
`Device root path ${this.deviceRootPath} does not exist.`);
|
||||
}
|
||||
|
||||
// Init and update iot workbench project file
|
||||
this.iotWorkbenchProjectFilePath =
|
||||
path.join(this.deviceRootPath, FileNames.iotWorkbenchProjectFileName);
|
||||
await updateProjectHostTypeConfig(
|
||||
scaffoldType, this.iotWorkbenchProjectFilePath, this.projectHostType);
|
||||
scaffoldType, this.iotWorkbenchProjectFilePath, this.projectHostType);
|
||||
|
||||
// Init workspace config file
|
||||
this.loadAndInitWorkspaceConfigFilePath(scaffoldType);
|
||||
|
@ -93,7 +93,7 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
const boardId = ConfigHandler.get<string>(ConfigKey.boardId);
|
||||
if (!boardId) {
|
||||
throw new Error(
|
||||
`Internal Error: Fail to get board id from configuration.`);
|
||||
`Internal Error: Fail to get board id from configuration.`);
|
||||
}
|
||||
await this.initDevice(boardId, scaffoldType);
|
||||
|
||||
|
@ -106,52 +106,52 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
}
|
||||
|
||||
async create(
|
||||
templateFilesInfo: TemplateFileInfo[], projectType: ProjectTemplateType,
|
||||
boardId: string, openInNewWindow: boolean): Promise<void> {
|
||||
templateFilesInfo: TemplateFileInfo[], projectType: ProjectTemplateType,
|
||||
boardId: string, openInNewWindow: boolean): Promise<void> {
|
||||
const createTimeScaffoldType = ScaffoldType.Local;
|
||||
|
||||
// Init device root path
|
||||
this.deviceRootPath =
|
||||
path.join(this.projectRootPath, folderName.deviceDefaultFolderName);
|
||||
if (!await FileUtility.directoryExists(
|
||||
createTimeScaffoldType, this.deviceRootPath)) {
|
||||
createTimeScaffoldType, this.deviceRootPath)) {
|
||||
await FileUtility.mkdirRecursively(
|
||||
createTimeScaffoldType, this.deviceRootPath);
|
||||
createTimeScaffoldType, this.deviceRootPath);
|
||||
}
|
||||
// Init iot workbench project file path
|
||||
this.iotWorkbenchProjectFilePath =
|
||||
path.join(this.deviceRootPath, FileNames.iotWorkbenchProjectFileName);
|
||||
// Init workspace config file
|
||||
this.workspaceConfigFilePath = path.join(
|
||||
this.projectRootPath,
|
||||
`${path.basename(this.projectRootPath)}${
|
||||
FileNames.workspaceExtensionName}`);
|
||||
this.projectRootPath,
|
||||
`${path.basename(this.projectRootPath)}${
|
||||
FileNames.workspaceExtensionName}`);
|
||||
|
||||
// Update iot workbench project file.
|
||||
await updateProjectHostTypeConfig(
|
||||
createTimeScaffoldType, this.iotWorkbenchProjectFilePath,
|
||||
this.projectHostType);
|
||||
createTimeScaffoldType, this.iotWorkbenchProjectFilePath,
|
||||
this.projectHostType);
|
||||
|
||||
const workspace: Workspace = {folders: [], settings: {}};
|
||||
const workspace: Workspace = { folders: [], settings: {} };
|
||||
|
||||
// Init device
|
||||
await this.initDevice(boardId, createTimeScaffoldType, templateFilesInfo);
|
||||
workspace.folders.push({path: folderName.deviceDefaultFolderName});
|
||||
workspace.folders.push({ path: folderName.deviceDefaultFolderName });
|
||||
workspace.settings[`IoTWorkbench.${ConfigKey.boardId}`] = boardId;
|
||||
workspace.settings[`IoTWorkbench.${ConfigKey.devicePath}`] =
|
||||
folderName.deviceDefaultFolderName;
|
||||
|
||||
// Create azure components
|
||||
await this.createAzureComponentsWithProjectType(
|
||||
projectType, createTimeScaffoldType, workspace);
|
||||
projectType, createTimeScaffoldType, workspace);
|
||||
|
||||
// Update workspace config to workspace config file
|
||||
if (!this.workspaceConfigFilePath) {
|
||||
throw new Error(
|
||||
`Workspace config file path is empty. Please initialize the project first.`);
|
||||
`Workspace config file path is empty. Please initialize the project first.`);
|
||||
}
|
||||
await FileUtility.writeJsonFile(
|
||||
createTimeScaffoldType, this.workspaceConfigFilePath, workspace);
|
||||
createTimeScaffoldType, this.workspaceConfigFilePath, workspace);
|
||||
|
||||
// Check components prerequisites
|
||||
this.componentList.forEach(async item => {
|
||||
|
@ -173,18 +173,18 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
|
||||
// Open project
|
||||
await this.openProject(
|
||||
createTimeScaffoldType, openInNewWindow, OpenScenario.createNewProject);
|
||||
createTimeScaffoldType, openInNewWindow, OpenScenario.createNewProject);
|
||||
}
|
||||
|
||||
async openProject(
|
||||
scaffoldType: ScaffoldType, openInNewWindow: boolean,
|
||||
openScenario: OpenScenario): Promise<void> {
|
||||
scaffoldType: ScaffoldType, openInNewWindow: boolean,
|
||||
openScenario: OpenScenario): Promise<void> {
|
||||
this.loadAndInitWorkspaceConfigFilePath(scaffoldType);
|
||||
|
||||
if (!await FileUtility.fileExists(
|
||||
scaffoldType, this.workspaceConfigFilePath)) {
|
||||
scaffoldType, this.workspaceConfigFilePath)) {
|
||||
throw new Error(`Workspace config file ${
|
||||
this.workspaceConfigFilePath} does not exist. Please initialize the project first.`);
|
||||
this.workspaceConfigFilePath} does not exist. Please initialize the project first.`);
|
||||
}
|
||||
|
||||
if (!openInNewWindow) {
|
||||
|
@ -194,8 +194,8 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
const telemetryWorker =
|
||||
TelemetryWorker.getInstance(this.extensionContext);
|
||||
const eventNames = openScenario === OpenScenario.createNewProject ?
|
||||
EventNames.createNewProjectEvent :
|
||||
EventNames.configProjectEnvironmentEvent;
|
||||
EventNames.createNewProjectEvent :
|
||||
EventNames.configProjectEnvironmentEvent;
|
||||
telemetryWorker.sendEvent(eventNames, this.telemetryContext);
|
||||
} catch {
|
||||
// If sending telemetry failed, skip the error to avoid blocking user.
|
||||
|
@ -203,8 +203,8 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
}
|
||||
|
||||
vscode.commands.executeCommand(
|
||||
IoTCubeCommands.OpenLocally, this.workspaceConfigFilePath,
|
||||
openInNewWindow);
|
||||
IoTCubeCommands.OpenLocally, this.workspaceConfigFilePath,
|
||||
openInNewWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,26 +214,23 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
* @param scaffoldType scaffold type
|
||||
* @param templateFilesInfo template files info to scaffold files for device
|
||||
*/
|
||||
private async initDevice(
|
||||
boardId: string, scaffoldType: ScaffoldType,
|
||||
templateFilesInfo?: TemplateFileInfo[]) {
|
||||
private async initDevice(boardId: string, scaffoldType: ScaffoldType, templateFilesInfo?: TemplateFileInfo[]): Promise<void> {
|
||||
if (!await FileUtility.directoryExists(scaffoldType, this.deviceRootPath)) {
|
||||
throw new Error(`Device root path ${
|
||||
this.deviceRootPath} does not exist. Please initialize the project first.`);
|
||||
this.deviceRootPath} does not exist. Please initialize the project first.`);
|
||||
}
|
||||
|
||||
let device: Component;
|
||||
if (boardId === az3166DeviceModule.AZ3166Device.boardId) {
|
||||
device = new az3166DeviceModule.AZ3166Device(
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.deviceRootPath, templateFilesInfo);
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.deviceRootPath, templateFilesInfo);
|
||||
} else if (boardId === ioTButtonDeviceModule.IoTButtonDevice.boardId) {
|
||||
device = new ioTButtonDeviceModule.IoTButtonDevice(
|
||||
this.extensionContext, this.deviceRootPath, templateFilesInfo);
|
||||
device = new ioTButtonDeviceModule.IoTButtonDevice(this.deviceRootPath, templateFilesInfo);
|
||||
} else if (boardId === esp32DeviceModule.Esp32Device.boardId) {
|
||||
device = new esp32DeviceModule.Esp32Device(
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.deviceRootPath, templateFilesInfo);
|
||||
this.extensionContext, this.channel, this.telemetryContext,
|
||||
this.deviceRootPath, templateFilesInfo);
|
||||
} else {
|
||||
throw new Error(`The board ${boardId} is not supported.`);
|
||||
}
|
||||
|
@ -266,10 +263,10 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
if (functionPath) {
|
||||
const functionLocation = path.join(this.projectRootPath, functionPath);
|
||||
const functionApp = new azureFunctionsModule.AzureFunctions(
|
||||
functionLocation, functionPath, this.channel, null, [{
|
||||
component: iotHub,
|
||||
type: azureComponentConfigModule.DependencyType.Input
|
||||
}]);
|
||||
functionLocation, functionPath, this.channel, null, [{
|
||||
component: iotHub,
|
||||
type: azureComponentConfigModule.DependencyType.Input
|
||||
}]);
|
||||
await functionApp.updateConfigSettings(scaffoldType);
|
||||
await functionApp.load();
|
||||
this.componentList.push(functionApp);
|
||||
|
@ -283,85 +280,85 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
* @param componentConfigs azure component configs
|
||||
*/
|
||||
private async initAzureComponentsWithConfig(
|
||||
scaffoldType: ScaffoldType,
|
||||
componentConfigs: AzureComponentConfig[]): Promise<void> {
|
||||
scaffoldType: ScaffoldType,
|
||||
componentConfigs: AzureComponentConfig[]): Promise<void> {
|
||||
this.validateProjectRootPath(scaffoldType);
|
||||
|
||||
const components: {[key: string]: Component} = {};
|
||||
for (const componentConfig of componentConfigs) {
|
||||
switch (componentConfig.type) {
|
||||
case ComponentType.IoTHub: {
|
||||
const iotHub =
|
||||
case ComponentType.IoTHub: {
|
||||
const iotHub =
|
||||
new ioTHubModule.IoTHub(this.projectRootPath, this.channel);
|
||||
await iotHub.load();
|
||||
components[iotHub.id] = iotHub;
|
||||
this.componentList.push(iotHub);
|
||||
await iotHub.load();
|
||||
components[iotHub.id] = iotHub;
|
||||
this.componentList.push(iotHub);
|
||||
|
||||
const iothubDevice =
|
||||
const iothubDevice =
|
||||
new ioTHubDeviceModule.IoTHubDevice(this.channel);
|
||||
this.componentList.push(iothubDevice);
|
||||
this.componentList.push(iothubDevice);
|
||||
|
||||
break;
|
||||
}
|
||||
case ComponentType.AzureFunctions: {
|
||||
const functionPath =
|
||||
break;
|
||||
}
|
||||
case ComponentType.AzureFunctions: {
|
||||
const functionPath =
|
||||
ConfigHandler.get<string>(ConfigKey.functionPath);
|
||||
if (!functionPath) {
|
||||
throw new Error(
|
||||
`Internal Error: Fail to get function path from configuration.`);
|
||||
}
|
||||
|
||||
const functionLocation =
|
||||
path.join(this.projectRootPath, functionPath);
|
||||
if (functionLocation) {
|
||||
const functionApp = new azureFunctionsModule.AzureFunctions(
|
||||
functionLocation, functionPath, this.channel);
|
||||
await functionApp.load();
|
||||
components[functionApp.id] = functionApp;
|
||||
this.componentList.push(functionApp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ComponentType.StreamAnalyticsJob: {
|
||||
const dependencies: Dependency[] = [];
|
||||
for (const dependent of componentConfig.dependencies) {
|
||||
const component = components[dependent.id];
|
||||
if (!component) {
|
||||
throw new Error(`Cannot find component with id ${dependent}.`);
|
||||
}
|
||||
dependencies.push({component, type: dependent.type});
|
||||
}
|
||||
const queryPath = path.join(
|
||||
this.projectRootPath, folderName.asaFolderName, 'query.asaql');
|
||||
const asa = new streamAnalyticsJobModule.StreamAnalyticsJob(
|
||||
queryPath, this.extensionContext, this.projectRootPath,
|
||||
this.channel, dependencies);
|
||||
await asa.load();
|
||||
components[asa.id] = asa;
|
||||
this.componentList.push(asa);
|
||||
break;
|
||||
}
|
||||
case ComponentType.CosmosDB: {
|
||||
const dependencies: Dependency[] = [];
|
||||
for (const dependent of componentConfig.dependencies) {
|
||||
const component = components[dependent.id];
|
||||
if (!component) {
|
||||
throw new Error(`Cannot find component with id ${dependent}.`);
|
||||
}
|
||||
dependencies.push({component, type: dependent.type});
|
||||
}
|
||||
const cosmosDB = new cosmosDBModule.CosmosDB(
|
||||
this.extensionContext, this.projectRootPath, this.channel,
|
||||
dependencies);
|
||||
await cosmosDB.load();
|
||||
components[cosmosDB.id] = cosmosDB;
|
||||
this.componentList.push(cosmosDB);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!functionPath) {
|
||||
throw new Error(
|
||||
`Component not supported with type of ${componentConfig.type}.`);
|
||||
`Internal Error: Fail to get function path from configuration.`);
|
||||
}
|
||||
|
||||
const functionLocation =
|
||||
path.join(this.projectRootPath, functionPath);
|
||||
if (functionLocation) {
|
||||
const functionApp = new azureFunctionsModule.AzureFunctions(
|
||||
functionLocation, functionPath, this.channel);
|
||||
await functionApp.load();
|
||||
components[functionApp.id] = functionApp;
|
||||
this.componentList.push(functionApp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ComponentType.StreamAnalyticsJob: {
|
||||
const dependencies: Dependency[] = [];
|
||||
for (const dependent of componentConfig.dependencies) {
|
||||
const component = components[dependent.id];
|
||||
if (!component) {
|
||||
throw new Error(`Cannot find component with id ${dependent}.`);
|
||||
}
|
||||
dependencies.push({ component, type: dependent.type });
|
||||
}
|
||||
const queryPath = path.join(
|
||||
this.projectRootPath, folderName.asaFolderName, 'query.asaql');
|
||||
const asa = new streamAnalyticsJobModule.StreamAnalyticsJob(
|
||||
queryPath, this.extensionContext, this.projectRootPath,
|
||||
this.channel, dependencies);
|
||||
await asa.load();
|
||||
components[asa.id] = asa;
|
||||
this.componentList.push(asa);
|
||||
break;
|
||||
}
|
||||
case ComponentType.CosmosDB: {
|
||||
const dependencies: Dependency[] = [];
|
||||
for (const dependent of componentConfig.dependencies) {
|
||||
const component = components[dependent.id];
|
||||
if (!component) {
|
||||
throw new Error(`Cannot find component with id ${dependent}.`);
|
||||
}
|
||||
dependencies.push({ component, type: dependent.type });
|
||||
}
|
||||
const cosmosDB = new cosmosDBModule.CosmosDB(
|
||||
this.extensionContext, this.projectRootPath, this.channel,
|
||||
dependencies);
|
||||
await cosmosDB.load();
|
||||
components[cosmosDB.id] = cosmosDB;
|
||||
this.componentList.push(cosmosDB);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(
|
||||
`Component not supported with type of ${componentConfig.type}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +372,7 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
|
||||
const azureConfigFileHandler =
|
||||
new azureComponentConfigModule.AzureConfigFileHandler(
|
||||
this.projectRootPath);
|
||||
this.projectRootPath);
|
||||
await azureConfigFileHandler.createIfNotExists(scaffoldType);
|
||||
|
||||
const componentConfigs =
|
||||
|
@ -390,101 +387,101 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
}
|
||||
|
||||
private async createAzureComponentsWithProjectType(
|
||||
projectType: ProjectTemplateType, scaffoldType: ScaffoldType,
|
||||
workspaceConfig: Workspace): Promise<void> {
|
||||
projectType: ProjectTemplateType, scaffoldType: ScaffoldType,
|
||||
workspaceConfig: Workspace): Promise<void> {
|
||||
this.validateProjectRootPath(scaffoldType);
|
||||
|
||||
// initialize the storage for azure component settings
|
||||
const azureConfigFileHandler =
|
||||
new azureComponentConfigModule.AzureConfigFileHandler(
|
||||
this.projectRootPath);
|
||||
this.projectRootPath);
|
||||
await azureConfigFileHandler.createIfNotExists(scaffoldType);
|
||||
|
||||
switch (projectType) {
|
||||
case ProjectTemplateType.Basic:
|
||||
// Save data to configFile
|
||||
break;
|
||||
case ProjectTemplateType.IotHub: {
|
||||
const iothub =
|
||||
case ProjectTemplateType.Basic:
|
||||
// Save data to configFile
|
||||
break;
|
||||
case ProjectTemplateType.IotHub: {
|
||||
const iothub =
|
||||
new ioTHubModule.IoTHub(this.projectRootPath, this.channel);
|
||||
this.componentList.push(iothub);
|
||||
break;
|
||||
this.componentList.push(iothub);
|
||||
break;
|
||||
}
|
||||
case ProjectTemplateType.AzureFunctions: {
|
||||
const iothub =
|
||||
new ioTHubModule.IoTHub(this.projectRootPath, this.channel);
|
||||
|
||||
const functionDir = path.join(
|
||||
this.projectRootPath, folderName.functionDefaultFolderName);
|
||||
if (!await FileUtility.directoryExists(scaffoldType, functionDir)) {
|
||||
await FileUtility.mkdirRecursively(scaffoldType, functionDir);
|
||||
}
|
||||
case ProjectTemplateType.AzureFunctions: {
|
||||
const iothub =
|
||||
new ioTHubModule.IoTHub(this.projectRootPath, this.channel);
|
||||
const azureFunctions = new azureFunctionsModule.AzureFunctions(
|
||||
functionDir, folderName.functionDefaultFolderName, this.channel,
|
||||
null, [{
|
||||
component: iothub,
|
||||
type: azureComponentConfigModule.DependencyType.Input
|
||||
}] /*Dependencies*/);
|
||||
|
||||
const functionDir = path.join(
|
||||
this.projectRootPath, folderName.functionDefaultFolderName);
|
||||
if (!await FileUtility.directoryExists(scaffoldType, functionDir)) {
|
||||
await FileUtility.mkdirRecursively(scaffoldType, functionDir);
|
||||
}
|
||||
const azureFunctions = new azureFunctionsModule.AzureFunctions(
|
||||
functionDir, folderName.functionDefaultFolderName, this.channel,
|
||||
null, [{
|
||||
component: iothub,
|
||||
type: azureComponentConfigModule.DependencyType.Input
|
||||
}] /*Dependencies*/);
|
||||
|
||||
workspaceConfig.folders.push(
|
||||
{path: folderName.functionDefaultFolderName});
|
||||
workspaceConfig.settings[`IoTWorkbench.${ConfigKey.functionPath}`] =
|
||||
workspaceConfig.folders.push(
|
||||
{ path: folderName.functionDefaultFolderName });
|
||||
workspaceConfig.settings[`IoTWorkbench.${ConfigKey.functionPath}`] =
|
||||
folderName.functionDefaultFolderName;
|
||||
|
||||
this.componentList.push(iothub);
|
||||
this.componentList.push(azureFunctions);
|
||||
break;
|
||||
}
|
||||
case ProjectTemplateType.StreamAnalytics: {
|
||||
const iothub =
|
||||
this.componentList.push(iothub);
|
||||
this.componentList.push(azureFunctions);
|
||||
break;
|
||||
}
|
||||
case ProjectTemplateType.StreamAnalytics: {
|
||||
const iothub =
|
||||
new ioTHubModule.IoTHub(this.projectRootPath, this.channel);
|
||||
|
||||
const cosmosDB = new cosmosDBModule.CosmosDB(
|
||||
this.extensionContext, this.projectRootPath, this.channel);
|
||||
const cosmosDB = new cosmosDBModule.CosmosDB(
|
||||
this.extensionContext, this.projectRootPath, this.channel);
|
||||
|
||||
const asaDir =
|
||||
const asaDir =
|
||||
path.join(this.projectRootPath, folderName.asaFolderName);
|
||||
if (!await FileUtility.directoryExists(scaffoldType, asaDir)) {
|
||||
await FileUtility.mkdirRecursively(scaffoldType, asaDir);
|
||||
}
|
||||
const asaFilePath = this.extensionContext.asAbsolutePath(
|
||||
path.join(FileNames.resourcesFolderName, 'asaql', 'query.asaql'));
|
||||
const queryPath = path.join(asaDir, 'query.asaql');
|
||||
const asaQueryContent =
|
||||
if (!await FileUtility.directoryExists(scaffoldType, asaDir)) {
|
||||
await FileUtility.mkdirRecursively(scaffoldType, asaDir);
|
||||
}
|
||||
const asaFilePath = this.extensionContext.asAbsolutePath(
|
||||
path.join(FileNames.resourcesFolderName, 'asaql', 'query.asaql'));
|
||||
const queryPath = path.join(asaDir, 'query.asaql');
|
||||
const asaQueryContent =
|
||||
fs.readFileSync(asaFilePath, 'utf8')
|
||||
.replace(/\[input\]/, `"iothub-${iothub.id}"`)
|
||||
.replace(/\[output\]/, `"cosmosdb-${cosmosDB.id}"`);
|
||||
await FileUtility.writeFile(scaffoldType, queryPath, asaQueryContent);
|
||||
.replace(/\[input\]/, `"iothub-${iothub.id}"`)
|
||||
.replace(/\[output\]/, `"cosmosdb-${cosmosDB.id}"`);
|
||||
await FileUtility.writeFile(scaffoldType, queryPath, asaQueryContent);
|
||||
|
||||
const asa = new streamAnalyticsJobModule.StreamAnalyticsJob(
|
||||
queryPath, this.extensionContext, this.projectRootPath,
|
||||
this.channel, [
|
||||
{
|
||||
component: iothub,
|
||||
type: azureComponentConfigModule.DependencyType.Input
|
||||
},
|
||||
{
|
||||
component: cosmosDB,
|
||||
type: azureComponentConfigModule.DependencyType.Other
|
||||
}
|
||||
]);
|
||||
const asa = new streamAnalyticsJobModule.StreamAnalyticsJob(
|
||||
queryPath, this.extensionContext, this.projectRootPath,
|
||||
this.channel, [
|
||||
{
|
||||
component: iothub,
|
||||
type: azureComponentConfigModule.DependencyType.Input
|
||||
},
|
||||
{
|
||||
component: cosmosDB,
|
||||
type: azureComponentConfigModule.DependencyType.Other
|
||||
}
|
||||
]);
|
||||
|
||||
workspaceConfig.folders.push({path: folderName.asaFolderName});
|
||||
workspaceConfig.settings[`IoTWorkbench.${ConfigKey.asaPath}`] =
|
||||
workspaceConfig.folders.push({ path: folderName.asaFolderName });
|
||||
workspaceConfig.settings[`IoTWorkbench.${ConfigKey.asaPath}`] =
|
||||
folderName.asaFolderName;
|
||||
|
||||
this.componentList.push(iothub);
|
||||
this.componentList.push(cosmosDB);
|
||||
this.componentList.push(asa);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
this.componentList.push(iothub);
|
||||
this.componentList.push(cosmosDB);
|
||||
this.componentList.push(asa);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Init workspace config file path at load time
|
||||
private async loadAndInitWorkspaceConfigFilePath(scaffoldType: ScaffoldType) {
|
||||
private async loadAndInitWorkspaceConfigFilePath(scaffoldType: ScaffoldType): Promise<void> {
|
||||
this.validateProjectRootPath(scaffoldType);
|
||||
|
||||
const workspaceFile = getWorkspaceFile(this.projectRootPath);
|
||||
|
@ -493,8 +490,8 @@ export class IoTWorkspaceProject extends IoTWorkbenchProjectBase {
|
|||
path.join(this.projectRootPath, workspaceFile);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Fail to init iot project workspace file path: Cannot find workspace file under project root path: ${
|
||||
this.projectRootPath}.`);
|
||||
`Fail to init iot project workspace file path: Cannot find workspace file under project root path: ${
|
||||
this.projectRootPath}.`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
import {crc16xmodem} from 'crc';
|
||||
import { crc16xmodem } from 'crc';
|
||||
import * as fs from 'fs-plus';
|
||||
|
||||
export class OTA {
|
||||
static generateCrc(filePath: string) {
|
||||
static generateCrc(filePath: string): {
|
||||
crc: string;
|
||||
size: number;
|
||||
} {
|
||||
const data = fs.readFileSync(filePath);
|
||||
const size = fs.statSync(filePath).size;
|
||||
const crc = crc16xmodem(data).toString(16);
|
||||
return {crc, size};
|
||||
return { crc, size };
|
||||
}
|
||||
}
|
|
@ -5,17 +5,17 @@ import * as path from 'path';
|
|||
import * as vscode from 'vscode';
|
||||
import * as sdk from 'vscode-iot-device-cube-sdk';
|
||||
|
||||
import {CancelOperationError} from '../CancelOperationError';
|
||||
import {ConfigHandler} from '../configHandler';
|
||||
import {ConfigKey, FileNames, OperationType, ScaffoldType} from '../constants';
|
||||
import {FileUtility} from '../FileUtility';
|
||||
import {TelemetryContext} from '../telemetry';
|
||||
import {askAndOpenInRemote, channelShowAndAppendLine, executeCommand} from '../utils';
|
||||
import { CancelOperationError } from '../CancelOperationError';
|
||||
import { ConfigHandler } from '../configHandler';
|
||||
import { ConfigKey, FileNames, OperationType, ScaffoldType } from '../constants';
|
||||
import { FileUtility } from '../FileUtility';
|
||||
import { TelemetryContext } from '../telemetry';
|
||||
import { askAndOpenInRemote, channelShowAndAppendLine, executeCommand } from '../utils';
|
||||
|
||||
import {ContainerDeviceBase} from './ContainerDeviceBase';
|
||||
import {DeviceType} from './Interfaces/Device';
|
||||
import {TemplateFileInfo} from './Interfaces/ProjectTemplate';
|
||||
import {RemoteExtension} from './RemoteExtension';
|
||||
import { ContainerDeviceBase } from './ContainerDeviceBase';
|
||||
import { DeviceType } from './Interfaces/Device';
|
||||
import { TemplateFileInfo } from './Interfaces/ProjectTemplate';
|
||||
import { RemoteExtension } from './RemoteExtension';
|
||||
|
||||
class RaspberryPiUploadConfig {
|
||||
static host = 'hostname';
|
||||
|
@ -30,17 +30,17 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
private static _boardId = 'raspberrypi';
|
||||
name = 'Raspberry Pi';
|
||||
|
||||
static get boardId() {
|
||||
static get boardId(): string {
|
||||
return RaspberryPiDevice._boardId;
|
||||
}
|
||||
|
||||
constructor(
|
||||
context: vscode.ExtensionContext, projectPath: string,
|
||||
channel: vscode.OutputChannel, telemetryContext: TelemetryContext,
|
||||
templateFilesInfo: TemplateFileInfo[] = []) {
|
||||
context: vscode.ExtensionContext, projectPath: string,
|
||||
channel: vscode.OutputChannel, telemetryContext: TelemetryContext,
|
||||
templateFilesInfo: TemplateFileInfo[] = []) {
|
||||
super(
|
||||
context, projectPath, channel, telemetryContext,
|
||||
DeviceType.Raspberry_Pi, templateFilesInfo);
|
||||
context, projectPath, channel, telemetryContext,
|
||||
DeviceType.RaspberryPi, templateFilesInfo);
|
||||
}
|
||||
|
||||
private async getBinaryFileName(): Promise<string|undefined> {
|
||||
|
@ -51,19 +51,19 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
return;
|
||||
}
|
||||
const getBinaryFileNameCmd = `cat ${
|
||||
cmakeFilePath} | grep 'add_executable' | sed -e 's/^add_executable(//' | awk '{$1=$1};1' | cut -d ' ' -f1 | tr -d '\n'`;
|
||||
cmakeFilePath} | grep 'add_executable' | sed -e 's/^add_executable(//' | awk '{$1=$1};1' | cut -d ' ' -f1 | tr -d '\n'`;
|
||||
|
||||
const binaryName = await executeCommand(getBinaryFileNameCmd);
|
||||
return binaryName;
|
||||
}
|
||||
|
||||
private async enableBinaryExecutability(ssh: sdk.SSH, binaryName: string) {
|
||||
private async enableBinaryExecutability(ssh: sdk.SSH, binaryName: string): Promise<void> {
|
||||
if (!binaryName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chmodCmd = `cd ${RaspberryPiUploadConfig.projectPath} && [ -f ${
|
||||
binaryName} ] && chmod +x ${binaryName}`;
|
||||
binaryName} ] && chmod +x ${binaryName}`;
|
||||
await ssh.exec(chmodCmd);
|
||||
|
||||
return;
|
||||
|
@ -80,7 +80,7 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
const binaryName = await this.getBinaryFileName();
|
||||
if (!binaryName) {
|
||||
const message = `No executable file specified in ${
|
||||
FileNames.cmakeFileName}. Nothing to upload to target machine.`;
|
||||
FileNames.cmakeFileName}. Nothing to upload to target machine.`;
|
||||
vscode.window.showWarningMessage(message);
|
||||
channelShowAndAppendLine(this.channel, message);
|
||||
return false;
|
||||
|
@ -88,9 +88,9 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
|
||||
const binaryFilePath = path.join(this.outputPath, binaryName);
|
||||
if (!await FileUtility.fileExists(
|
||||
ScaffoldType.Workspace, binaryFilePath)) {
|
||||
ScaffoldType.Workspace, binaryFilePath)) {
|
||||
const message = `Executable file ${binaryName} does not exist under ${
|
||||
this.outputPath}. Please compile device code first.`;
|
||||
this.outputPath}. Please compile device code first.`;
|
||||
vscode.window.showWarningMessage(message);
|
||||
channelShowAndAppendLine(this.channel, message);
|
||||
return false;
|
||||
|
@ -105,16 +105,16 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
|
||||
const ssh = new sdk.SSH();
|
||||
await ssh.open(
|
||||
RaspberryPiUploadConfig.host, RaspberryPiUploadConfig.port,
|
||||
RaspberryPiUploadConfig.user, RaspberryPiUploadConfig.password);
|
||||
RaspberryPiUploadConfig.host, RaspberryPiUploadConfig.port,
|
||||
RaspberryPiUploadConfig.user, RaspberryPiUploadConfig.password);
|
||||
|
||||
try {
|
||||
await ssh.uploadFile(
|
||||
binaryFilePath, RaspberryPiUploadConfig.projectPath);
|
||||
binaryFilePath, RaspberryPiUploadConfig.projectPath);
|
||||
} catch (error) {
|
||||
const message =
|
||||
`SSH traffic is too busy. Please wait a second and retry. Error: ${
|
||||
error}.`;
|
||||
error}.`;
|
||||
channelShowAndAppendLine(this.channel, message);
|
||||
console.log(error);
|
||||
throw new Error(message);
|
||||
|
@ -124,14 +124,14 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
await this.enableBinaryExecutability(ssh, binaryName);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to enable binary executability. Error: ${error.message}`);
|
||||
`Failed to enable binary executability. Error: ${error.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await ssh.close();
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to close SSH connection. Error: ${error.message}`);
|
||||
`Failed to close SSH connection. Error: ${error.message}`);
|
||||
}
|
||||
|
||||
const message = `Successfully deploy compiled files to device board.`;
|
||||
|
@ -139,8 +139,8 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
vscode.window.showInformationMessage(message);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Upload binary file to device ${RaspberryPiUploadConfig.user}@${
|
||||
RaspberryPiUploadConfig.host} failed. ${error}`);
|
||||
`Upload binary file to device ${RaspberryPiUploadConfig.user}@${
|
||||
RaspberryPiUploadConfig.host} failed. ${error}`);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -199,14 +199,14 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
});
|
||||
|
||||
sshDevicePickItems.push(
|
||||
{
|
||||
label: '$(sync) Discover again',
|
||||
detail: 'Auto discover SSH enabled device in LAN'
|
||||
},
|
||||
{
|
||||
label: '$(gear) Manual setup',
|
||||
detail: 'Setup device SSH configuration manually'
|
||||
});
|
||||
{
|
||||
label: '$(sync) Discover again',
|
||||
detail: 'Auto discover SSH enabled device in LAN'
|
||||
},
|
||||
{
|
||||
label: '$(gear) Manual setup',
|
||||
detail: 'Setup device SSH configuration manually'
|
||||
});
|
||||
|
||||
return sshDevicePickItems;
|
||||
}
|
||||
|
@ -283,8 +283,8 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
return false;
|
||||
}
|
||||
const raspiPort = raspiPortString && !isNaN(Number(raspiPortString)) ?
|
||||
Number(raspiPortString) :
|
||||
RaspberryPiUploadConfig.port;
|
||||
Number(raspiPortString) :
|
||||
RaspberryPiUploadConfig.port;
|
||||
|
||||
// Raspberry Pi user name
|
||||
const raspiUserOption: vscode.InputBoxOptions = {
|
||||
|
@ -335,7 +335,7 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
const projectFolderPath = this.projectFolder;
|
||||
|
||||
if (!FileUtility.directoryExists(
|
||||
ScaffoldType.Workspace, projectFolderPath)) {
|
||||
ScaffoldType.Workspace, projectFolderPath)) {
|
||||
throw new Error('Unable to find the device folder inside the project.');
|
||||
}
|
||||
|
||||
|
@ -358,13 +358,13 @@ export class RaspberryPiDevice extends ContainerDeviceBase {
|
|||
ConfigHandler.get<string>(ConfigKey.iotHubDeviceConnectionString);
|
||||
if (!deviceConnectionString) {
|
||||
throw new Error(
|
||||
'Unable to get the device connection string, please invoke the command of Azure Provision first.');
|
||||
'Unable to get the device connection string, please invoke the command of Azure Provision first.');
|
||||
}
|
||||
const ssh = new sdk.SSH();
|
||||
await ssh.clipboardCopy(deviceConnectionString);
|
||||
await ssh.close();
|
||||
vscode.window.showInformationMessage(
|
||||
'Device connection string has been copied.');
|
||||
'Device connection string has been copied.');
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -2,14 +2,14 @@
|
|||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import {DependentExtensions} from '../constants';
|
||||
import {DialogResponses} from '../DialogResponses';
|
||||
import {WorkbenchExtension} from '../WorkbenchExtension';
|
||||
import {VscodeCommands} from '../common/Commands';
|
||||
import {CancelOperationError} from '../CancelOperationError';
|
||||
import { DependentExtensions } from '../constants';
|
||||
import { DialogResponses } from '../DialogResponses';
|
||||
import { WorkbenchExtension } from '../WorkbenchExtension';
|
||||
import { VscodeCommands } from '../common/Commands';
|
||||
import { CancelOperationError } from '../CancelOperationError';
|
||||
|
||||
export class RemoteExtension {
|
||||
static isRemote(context: vscode.ExtensionContext) {
|
||||
static isRemote(context: vscode.ExtensionContext): boolean {
|
||||
const extension = WorkbenchExtension.getExtension(context);
|
||||
if (!extension) {
|
||||
throw new Error('Fail to get workbench extension.');
|
||||
|
@ -28,11 +28,11 @@ export class RemoteExtension {
|
|||
const message =
|
||||
'Remote extension is required for the current project. Do you want to install it from marketplace?';
|
||||
const choice = await vscode.window.showInformationMessage(
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
if (choice === DialogResponses.yes) {
|
||||
vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen,
|
||||
vscode.Uri.parse('vscode:extension/' + DependentExtensions.remote));
|
||||
VscodeCommands.VscodeOpen,
|
||||
vscode.Uri.parse('vscode:extension/' + DependentExtensions.remote));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ export class RemoteExtension {
|
|||
const res = await RemoteExtension.isAvailable();
|
||||
if (!res) {
|
||||
throw new CancelOperationError(
|
||||
`Remote extension is not available. Please install ${
|
||||
DependentExtensions.remote} first.`);
|
||||
`Remote extension is not available. Please install ${
|
||||
DependentExtensions.remote} first.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as fs from 'fs-plus';
|
|||
import * as path from 'path';
|
||||
import * as ssh2 from 'ssh2';
|
||||
import * as vscode from 'vscode';
|
||||
import {channelShowAndAppendLine} from '../utils';
|
||||
import { channelShowAndAppendLine } from '../utils';
|
||||
|
||||
export class SSH {
|
||||
private _client: ssh2.Client;
|
||||
|
@ -19,276 +19,271 @@ export class SSH {
|
|||
}
|
||||
}
|
||||
|
||||
async connect(
|
||||
host: string, port: number, username: string, password: string) {
|
||||
async connect(host: string, port: number, username: string, password: string): Promise<boolean> {
|
||||
return new Promise(
|
||||
(resolve: (value: boolean) => void,
|
||||
reject: (reason: boolean) => void) => {
|
||||
const conn = this._client;
|
||||
conn.on('ready',
|
||||
() => {
|
||||
this._connected = true;
|
||||
return resolve(true);
|
||||
})
|
||||
.on('end',
|
||||
() => {
|
||||
this._connected = false;
|
||||
return resolve(false);
|
||||
})
|
||||
.on('close',
|
||||
() => {
|
||||
this._connected = false;
|
||||
return resolve(false);
|
||||
})
|
||||
.connect({host, port, username, password});
|
||||
});
|
||||
(resolve: (value: boolean) => void) => {
|
||||
const conn = this._client;
|
||||
conn.on('ready',
|
||||
() => {
|
||||
this._connected = true;
|
||||
return resolve(true);
|
||||
})
|
||||
.on('end',
|
||||
() => {
|
||||
this._connected = false;
|
||||
return resolve(false);
|
||||
})
|
||||
.on('close',
|
||||
() => {
|
||||
this._connected = false;
|
||||
return resolve(false);
|
||||
})
|
||||
.connect({ host, port, username, password });
|
||||
});
|
||||
}
|
||||
|
||||
async upload(filePath: string, remoteRootPath: string) {
|
||||
async upload(filePath: string, remoteRootPath: string): Promise<boolean> {
|
||||
return new Promise(
|
||||
(resolve: (value: boolean) => void,
|
||||
reject: (reason: boolean) => void) => {
|
||||
if (!this._connected) {
|
||||
return resolve(false);
|
||||
}
|
||||
(resolve: (value: boolean) => void) => {
|
||||
if (!this._connected) {
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return resolve(false);
|
||||
}
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
filePath = filePath.replace(/[\\\/]+/g, '/');
|
||||
filePath = filePath.replace(/[\\/]+/g, '/');
|
||||
|
||||
const rootPath =
|
||||
const rootPath =
|
||||
(fs.isDirectorySync(filePath) ? filePath : path.dirname(filePath))
|
||||
.replace(/\/$/, '');
|
||||
const files = fs.listTreeSync(filePath);
|
||||
.replace(/\/$/, '');
|
||||
const files = fs.listTreeSync(filePath);
|
||||
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(this._channel, '');
|
||||
}
|
||||
|
||||
const conn = this._client;
|
||||
conn.sftp(async (err, sftp) => {
|
||||
if (err) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(this._channel, `SFTP Error:`);
|
||||
channelShowAndAppendLine(this._channel, err.message);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
const rootPathExist = await this.isExist(sftp, remoteRootPath);
|
||||
|
||||
if (rootPathExist) {
|
||||
const overwriteOption =
|
||||
await vscode.window.showInformationMessage(
|
||||
`${remoteRootPath} exists, overwrite?`, 'Yes', 'No',
|
||||
'Cancel');
|
||||
if (overwriteOption === 'Cancel') {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, 'Device upload cancelled.');
|
||||
}
|
||||
vscode.window.showWarningMessage('Device upload cancelled.');
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
if (overwriteOption === 'No') {
|
||||
const raspiPathOption: vscode.InputBoxOptions = {
|
||||
value: 'IoTProject',
|
||||
prompt: `Please input Raspberry Pi path here.`,
|
||||
ignoreFocusOut: true
|
||||
};
|
||||
let raspiPath =
|
||||
await vscode.window.showInputBox(raspiPathOption);
|
||||
if (!raspiPath) {
|
||||
return false;
|
||||
}
|
||||
raspiPath = raspiPath || 'IoTProject';
|
||||
const res = await this.upload(filePath, raspiPath);
|
||||
return resolve(res);
|
||||
}
|
||||
|
||||
const rmDirRes = await this.shell(`rm -rf ${remoteRootPath}`);
|
||||
if (!rmDirRes) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel,
|
||||
`Directory Error: remove ${remoteRootPath} failed.`);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
const rootPathCreated = await this.ensureDir(sftp, remoteRootPath);
|
||||
|
||||
if (!rootPathCreated) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, `Directory Error: ${remoteRootPath}`);
|
||||
channelShowAndAppendLine(this._channel, err);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
const res = await this.uploadSingleFile(
|
||||
sftp, file, rootPath, remoteRootPath);
|
||||
if (!res) {
|
||||
return resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
return resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async isExist(sftp: ssh2.SFTPWrapper, remotePath: string): Promise<boolean> {
|
||||
return new Promise(
|
||||
(resolve: (value: boolean) => void) => {
|
||||
sftp.readdir(remotePath, (err) => {
|
||||
if (err) {
|
||||
return resolve(false);
|
||||
}
|
||||
return resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async ensureDir(sftp: ssh2.SFTPWrapper, remotePath: string): Promise<boolean> {
|
||||
return new Promise(
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
async (
|
||||
resolve: (value: boolean) => void) => {
|
||||
const dirExist = await this.isExist(sftp, remotePath);
|
||||
if (!dirExist) {
|
||||
sftp.mkdir(remotePath, async err => {
|
||||
if (err) {
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
return resolve(true);
|
||||
});
|
||||
}
|
||||
return resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
private async uploadSingleFile(sftp: ssh2.SFTPWrapper, filePath: string, rootPath: string, remoteRootPath: string): Promise<boolean> {
|
||||
return new Promise(
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
async (
|
||||
resolve: (value: boolean) => void) => {
|
||||
const relativePath =
|
||||
filePath.replace(/[\\/]+/g, '/').substr(rootPath.length + 1);
|
||||
if (/(^|\/)node_modules(\/|$)/.test(relativePath) ||
|
||||
/(^|\/).vscode(\/|$)/.test(relativePath) ||
|
||||
relativePath === '.iotworkbenchproject') {
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
const remotePath =
|
||||
path.join(remoteRootPath, relativePath).replace(/[\\/]+/g, '/');
|
||||
|
||||
if (fs.isDirectorySync(filePath)) {
|
||||
const pathCreated = await this.ensureDir(sftp, remotePath);
|
||||
if (!pathCreated) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, `Directory Error: ${relativePath}`);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
return resolve(true);
|
||||
} else {
|
||||
sftp.fastPut(filePath, remotePath, err => {
|
||||
if (err) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, `File Error: ${relativePath}`);
|
||||
}
|
||||
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, `File Uploaded: ${relativePath}`);
|
||||
}
|
||||
return resolve(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async shell(command: string, timeout?: number): Promise<boolean> {
|
||||
return new Promise(
|
||||
(resolve: (value: boolean) => void) => {
|
||||
if (!this._connected) {
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
let timeoutCounter: NodeJS.Timer;
|
||||
|
||||
if (timeout) {
|
||||
timeoutCounter = setTimeout(() => {
|
||||
return resolve(false);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
const conn = this._client;
|
||||
conn.shell((err, stream) => {
|
||||
if (err) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(this._channel, `Shell Error:`);
|
||||
channelShowAndAppendLine(this._channel, err.message);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(this._channel, '');
|
||||
}
|
||||
|
||||
const conn = this._client;
|
||||
conn.sftp(async (err, sftp) => {
|
||||
if (err) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(this._channel, `SFTP Error:`);
|
||||
channelShowAndAppendLine(this._channel, err.message);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
const rootPathExist = await this.isExist(sftp, remoteRootPath);
|
||||
|
||||
if (rootPathExist) {
|
||||
const overwriteOption =
|
||||
await vscode.window.showInformationMessage(
|
||||
`${remoteRootPath} exists, overwrite?`, 'Yes', 'No',
|
||||
'Cancel');
|
||||
if (overwriteOption === 'Cancel') {
|
||||
stream
|
||||
.on('close',
|
||||
() => {
|
||||
clearTimeout(timeoutCounter);
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, 'Device upload cancelled.');
|
||||
channelShowAndAppendLine(this._channel, '');
|
||||
}
|
||||
vscode.window.showWarningMessage('Device upload cancelled.');
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
if (overwriteOption === 'No') {
|
||||
const raspiPathOption: vscode.InputBoxOptions = {
|
||||
value: 'IoTProject',
|
||||
prompt: `Please input Raspberry Pi path here.`,
|
||||
ignoreFocusOut: true
|
||||
};
|
||||
let raspiPath =
|
||||
await vscode.window.showInputBox(raspiPathOption);
|
||||
if (!raspiPath) {
|
||||
return false;
|
||||
}
|
||||
raspiPath = raspiPath || 'IoTProject';
|
||||
const res = await this.upload(filePath, raspiPath);
|
||||
return resolve(res);
|
||||
}
|
||||
|
||||
const rmDirRes = await this.shell(`rm -rf ${remoteRootPath}`);
|
||||
if (!rmDirRes) {
|
||||
})
|
||||
.on('data',
|
||||
(data: string|Buffer) => {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel,
|
||||
`Directory Error: remove ${remoteRootPath} failed.`);
|
||||
const output = data.toString().replace(
|
||||
// eslint-disable-next-line no-control-regex
|
||||
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
|
||||
'');
|
||||
this._channel.append(output);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
const rootPathCreated = await this.ensureDir(sftp, remoteRootPath);
|
||||
|
||||
if (!rootPathCreated) {
|
||||
})
|
||||
.stderr.on('data', (data: string|Buffer) => {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, `Directory Error: ${remoteRootPath}`);
|
||||
channelShowAndAppendLine(this._channel, err);
|
||||
const output = data.toString().replace(
|
||||
// eslint-disable-next-line no-control-regex
|
||||
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
|
||||
'');
|
||||
this._channel.append(output);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
const res = await this.uploadSingleFile(
|
||||
sftp, file, rootPath, remoteRootPath);
|
||||
if (!res) {
|
||||
return resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
return resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async isExist(sftp: ssh2.SFTPWrapper, remotePath: string) {
|
||||
return new Promise(
|
||||
(resolve: (value: boolean) => void,
|
||||
reject: (reason: boolean) => void) => {
|
||||
sftp.readdir(remotePath, (err, list) => {
|
||||
if (err) {
|
||||
return resolve(false);
|
||||
}
|
||||
return resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async ensureDir(sftp: ssh2.SFTPWrapper, remotePath: string) {
|
||||
return new Promise(
|
||||
async (
|
||||
resolve: (value: boolean) => void,
|
||||
reject: (reason: boolean) => void) => {
|
||||
const dirExist = await this.isExist(sftp, remotePath);
|
||||
if (!dirExist) {
|
||||
sftp.mkdir(remotePath, async err => {
|
||||
if (err) {
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
return resolve(true);
|
||||
});
|
||||
}
|
||||
return resolve(true);
|
||||
|
||||
stream.setWindow(10, 500, 10, 100);
|
||||
stream.end(command + '\nexit\n');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async uploadSingleFile(
|
||||
sftp: ssh2.SFTPWrapper, filePath: string, rootPath: string,
|
||||
remoteRootPath: string) {
|
||||
return new Promise(
|
||||
async (
|
||||
resolve: (value: boolean) => void,
|
||||
reject: (reason: boolean) => void) => {
|
||||
const relativePath =
|
||||
filePath.replace(/[\\\/]+/g, '/').substr(rootPath.length + 1);
|
||||
if (/(^|\/)node_modules(\/|$)/.test(relativePath) ||
|
||||
/(^|\/).vscode(\/|$)/.test(relativePath) ||
|
||||
relativePath === '.iotworkbenchproject') {
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
const remotePath =
|
||||
path.join(remoteRootPath, relativePath).replace(/[\\\/]+/g, '/');
|
||||
|
||||
if (fs.isDirectorySync(filePath)) {
|
||||
const pathCreated = await this.ensureDir(sftp, remotePath);
|
||||
if (!pathCreated) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, `Directory Error: ${relativePath}`);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
return resolve(true);
|
||||
} else {
|
||||
sftp.fastPut(filePath, remotePath, err => {
|
||||
if (err) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, `File Error: ${relativePath}`);
|
||||
}
|
||||
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(
|
||||
this._channel, `File Uploaded: ${relativePath}`);
|
||||
}
|
||||
return resolve(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async shell(command: string, timeout?: number) {
|
||||
return new Promise(
|
||||
(resolve: (value: boolean) => void,
|
||||
reject: (reason: boolean) => void) => {
|
||||
if (!this._connected) {
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
let timeoutCounter: NodeJS.Timer;
|
||||
|
||||
if (timeout) {
|
||||
timeoutCounter = setTimeout(() => {
|
||||
return resolve(false);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
const conn = this._client;
|
||||
conn.shell((err, stream) => {
|
||||
if (err) {
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(this._channel, `Shell Error:`);
|
||||
channelShowAndAppendLine(this._channel, err.message);
|
||||
}
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(this._channel, '');
|
||||
}
|
||||
|
||||
stream
|
||||
.on('close',
|
||||
() => {
|
||||
clearTimeout(timeoutCounter);
|
||||
if (this._channel) {
|
||||
channelShowAndAppendLine(this._channel, '');
|
||||
}
|
||||
return resolve(true);
|
||||
})
|
||||
.on('data',
|
||||
(data: string|Buffer) => {
|
||||
if (this._channel) {
|
||||
const output = data.toString().replace(
|
||||
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
|
||||
'');
|
||||
this._channel.append(output);
|
||||
}
|
||||
})
|
||||
.stderr.on('data', (data: string|Buffer) => {
|
||||
if (this._channel) {
|
||||
const output = data.toString().replace(
|
||||
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
|
||||
'');
|
||||
this._channel.append(output);
|
||||
}
|
||||
});
|
||||
|
||||
stream.setWindow(10, 500, 10, 100);
|
||||
stream.end(command + '\nexit\n');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async close() {
|
||||
async close(): Promise<boolean> {
|
||||
this._client.end();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import {ResourceManagementClient} from 'azure-arm-resource';
|
||||
import { ResourceManagementClient } from 'azure-arm-resource';
|
||||
import * as fs from 'fs-plus';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import { Guid } from 'guid-typescript';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {AzureComponentsStorage, FileNames, ScaffoldType} from '../constants';
|
||||
import {channelPrintJsonObject, channelShowAndAppendLine} from '../utils';
|
||||
import { AzureComponentsStorage, FileNames, ScaffoldType } from '../constants';
|
||||
import { channelPrintJsonObject, channelShowAndAppendLine } from '../utils';
|
||||
|
||||
import {AzureComponentConfig, AzureConfigFileHandler, AzureConfigs, ComponentInfo, Dependency, DependencyConfig, DependencyType} from './AzureComponentConfig';
|
||||
import {ARMTemplate, AzureUtility} from './AzureUtility';
|
||||
import {Component, ComponentType} from './Interfaces/Component';
|
||||
import {Deployable} from './Interfaces/Deployable';
|
||||
import {Provisionable} from './Interfaces/Provisionable';
|
||||
import { AzureComponentConfig, AzureConfigFileHandler, AzureConfigs, ComponentInfo, Dependency, DependencyConfig, DependencyType } from './AzureComponentConfig';
|
||||
import { ARMTemplate, AzureUtility } from './AzureUtility';
|
||||
import { Component, ComponentType } from './Interfaces/Component';
|
||||
import { Deployable } from './Interfaces/Deployable';
|
||||
import { Provisionable } from './Interfaces/Provisionable';
|
||||
|
||||
enum StreamAnalyticsAction {
|
||||
Start = 1,
|
||||
|
@ -34,17 +34,17 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
private azureClient: ResourceManagementClient|null = null;
|
||||
private catchedStreamAnalyticsList: Array<{name: string}> = [];
|
||||
|
||||
private async initAzureClient() {
|
||||
private async initAzureClient(): Promise<ResourceManagementClient> {
|
||||
if (this.subscriptionId && this.resourceGroup &&
|
||||
this.streamAnalyticsJobName && this.azureClient) {
|
||||
return this.azureClient;
|
||||
}
|
||||
|
||||
const componentConfig = await this.azureConfigHandler.getComponentById(
|
||||
ScaffoldType.Workspace, this.id);
|
||||
ScaffoldType.Workspace, this.id);
|
||||
if (!componentConfig) {
|
||||
throw new Error(
|
||||
`Cannot find Azure Stream Analytics component with id ${this.id}.`);
|
||||
`Cannot find Azure Stream Analytics component with id ${this.id}.`);
|
||||
}
|
||||
|
||||
const componentInfo = componentConfig.componentInfo;
|
||||
|
@ -69,16 +69,17 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
return azureClient;
|
||||
}
|
||||
|
||||
private async callAction(action: StreamAnalyticsAction) {
|
||||
private async callAction(action: StreamAnalyticsAction): Promise<unknown> {
|
||||
const actionResource = `/subscriptions/${
|
||||
this.subscriptionId}/resourceGroups/${
|
||||
this.resourceGroup}/providers/Microsoft.StreamAnalytics/streamingjobs/${
|
||||
this.streamAnalyticsJobName}/${
|
||||
StreamAnalyticsAction[action].toLowerCase()}?api-version=2015-10-01`;
|
||||
this.subscriptionId}/resourceGroups/${
|
||||
this.resourceGroup}/providers/Microsoft.StreamAnalytics/streamingjobs/${
|
||||
this.streamAnalyticsJobName}/${
|
||||
StreamAnalyticsAction[action].toLowerCase()}?api-version=2015-10-01`;
|
||||
await AzureUtility.postRequest(actionResource);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
clearTimeout(timer);
|
||||
return resolve(false);
|
||||
}, 10 * 60 * 1000);
|
||||
|
@ -96,35 +97,35 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
});
|
||||
}
|
||||
|
||||
private getStreamAnalyticsByNameFromCache(name: string) {
|
||||
private getStreamAnalyticsByNameFromCache(name: string): {name: string}|undefined {
|
||||
return this.catchedStreamAnalyticsList.find(item => item.name === name);
|
||||
}
|
||||
|
||||
private async getStreamAnalyticsInResourceGroup() {
|
||||
private async getStreamAnalyticsInResourceGroup(): Promise<vscode.QuickPickItem[]>{
|
||||
const resource = `/subscriptions/${
|
||||
AzureUtility.subscriptionId}/resourceGroups/${
|
||||
AzureUtility
|
||||
.resourceGroup}/providers/Microsoft.StreamAnalytics/streamingjobs?api-version=2015-10-01`;
|
||||
AzureUtility.subscriptionId}/resourceGroups/${
|
||||
AzureUtility
|
||||
.resourceGroup}/providers/Microsoft.StreamAnalytics/streamingjobs?api-version=2015-10-01`;
|
||||
const asaListRes = await AzureUtility.getRequest(resource) as
|
||||
{value: Array<{name: string, properties: {jobState: string}}>};
|
||||
{value: Array<{name: string; properties: {jobState: string}}>};
|
||||
const asaList: vscode.QuickPickItem[] =
|
||||
[{label: '$(plus) Create New Stream Analytics Job', description: ''}];
|
||||
[{ label: '$(plus) Create New Stream Analytics Job', description: '' }];
|
||||
for (const item of asaListRes.value) {
|
||||
asaList.push({label: item.name, description: item.properties.jobState});
|
||||
asaList.push({ label: item.name, description: item.properties.jobState });
|
||||
}
|
||||
|
||||
this.catchedStreamAnalyticsList = asaListRes.value;
|
||||
return asaList;
|
||||
}
|
||||
|
||||
get id() {
|
||||
get id(): string {
|
||||
return this.componentId;
|
||||
}
|
||||
|
||||
constructor(
|
||||
queryPath: string, context: vscode.ExtensionContext, projectRoot: string,
|
||||
channel: vscode.OutputChannel,
|
||||
dependencyComponents: Dependency[]|null = null) {
|
||||
queryPath: string, context: vscode.ExtensionContext, projectRoot: string,
|
||||
channel: vscode.OutputChannel,
|
||||
dependencyComponents: Dependency[]|null = null) {
|
||||
this.queryPath = queryPath;
|
||||
this.componentType = ComponentType.StreamAnalyticsJob;
|
||||
this.channel = channel;
|
||||
|
@ -134,8 +135,8 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
this.extensionContext = context;
|
||||
if (dependencyComponents && dependencyComponents.length > 0) {
|
||||
dependencyComponents.forEach(
|
||||
dependency => this.dependencies.push(
|
||||
{id: dependency.component.id, type: dependency.type}));
|
||||
dependency => this.dependencies.push(
|
||||
{ id: dependency.component.id, type: dependency.type }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,8 +152,8 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
|
||||
async load(): Promise<boolean> {
|
||||
const azureConfigFilePath = path.join(
|
||||
this.projectRootPath, AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
this.projectRootPath, AzureComponentsStorage.folderName,
|
||||
AzureComponentsStorage.fileName);
|
||||
|
||||
if (!fs.existsSync(azureConfigFilePath)) {
|
||||
return false;
|
||||
|
@ -163,7 +164,7 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
try {
|
||||
azureConfigs = JSON.parse(fs.readFileSync(azureConfigFilePath, 'utf8'));
|
||||
const asaConfig = azureConfigs.componentConfigs.find(
|
||||
config => config.type === this.componentType);
|
||||
config => config.type === this.componentType);
|
||||
if (asaConfig) {
|
||||
this.componentId = asaConfig.id;
|
||||
this.dependencies = asaConfig.dependencies;
|
||||
|
@ -188,7 +189,7 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
return;
|
||||
}
|
||||
await this.azureConfigHandler.updateComponent(
|
||||
type, asaComponentIndex, componentInfo);
|
||||
type, asaComponentIndex, componentInfo);
|
||||
} else {
|
||||
const newAsaConfig: AzureComponentConfig = {
|
||||
id: this.id,
|
||||
|
@ -206,8 +207,8 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
|
||||
const asaList = this.getStreamAnalyticsInResourceGroup();
|
||||
const asaNameChoose = await vscode.window.showQuickPick(
|
||||
asaList,
|
||||
{placeHolder: 'Select Stream Analytics Job', ignoreFocusOut: true});
|
||||
asaList,
|
||||
{ placeHolder: 'Select Stream Analytics Job', ignoreFocusOut: true });
|
||||
if (!asaNameChoose) {
|
||||
return false;
|
||||
}
|
||||
|
@ -217,10 +218,10 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
if (!asaNameChoose.description) {
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Creating Stream Analytics Job...');
|
||||
this.channel, 'Creating Stream Analytics Job...');
|
||||
}
|
||||
const asaArmTemplatePath = this.extensionContext.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, 'arm', 'streamanalytics.json'));
|
||||
FileNames.resourcesFolderName, 'arm', 'streamanalytics.json'));
|
||||
const asaArmTemplate =
|
||||
JSON.parse(fs.readFileSync(asaArmTemplatePath, 'utf8')) as
|
||||
ARMTemplate;
|
||||
|
@ -238,7 +239,7 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
} else {
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Creating Stream Analytics Job...');
|
||||
this.channel, 'Creating Stream Analytics Job...');
|
||||
}
|
||||
streamAnalyticsJobName = asaNameChoose.label;
|
||||
const asaDetail =
|
||||
|
@ -250,114 +251,114 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
|
||||
for (const dependency of this.dependencies) {
|
||||
const componentConfig = await this.azureConfigHandler.getComponentById(
|
||||
scaffoldType, dependency.id);
|
||||
scaffoldType, dependency.id);
|
||||
if (!componentConfig) {
|
||||
throw new Error(`Cannot find component with id ${dependency.id}.`);
|
||||
}
|
||||
|
||||
if (dependency.type === DependencyType.Input) {
|
||||
switch (componentConfig.type) {
|
||||
case 'IoTHub': {
|
||||
if (!componentConfig.componentInfo) {
|
||||
return false;
|
||||
}
|
||||
const iotHubConnectionString =
|
||||
case 'IoTHub': {
|
||||
if (!componentConfig.componentInfo) {
|
||||
return false;
|
||||
}
|
||||
const iotHubConnectionString =
|
||||
componentConfig.componentInfo.values.iotHubConnectionString;
|
||||
let iotHubName = '';
|
||||
let iotHubKeyName = '';
|
||||
let iotHubKey = '';
|
||||
const iotHubNameMatches =
|
||||
let iotHubName = '';
|
||||
let iotHubKeyName = '';
|
||||
let iotHubKey = '';
|
||||
const iotHubNameMatches =
|
||||
iotHubConnectionString.match(/HostName=(.*?)\./);
|
||||
const iotHubKeyMatches =
|
||||
const iotHubKeyMatches =
|
||||
iotHubConnectionString.match(/SharedAccessKey=(.*?)(;|$)/);
|
||||
const iotHubKeyNameMatches =
|
||||
const iotHubKeyNameMatches =
|
||||
iotHubConnectionString.match(/SharedAccessKeyName=(.*?)(;|$)/);
|
||||
if (iotHubNameMatches) {
|
||||
iotHubName = iotHubNameMatches[1];
|
||||
}
|
||||
if (iotHubKeyMatches) {
|
||||
iotHubKey = iotHubKeyMatches[1];
|
||||
}
|
||||
if (iotHubKeyNameMatches) {
|
||||
iotHubKeyName = iotHubKeyNameMatches[1];
|
||||
}
|
||||
if (iotHubNameMatches) {
|
||||
iotHubName = iotHubNameMatches[1];
|
||||
}
|
||||
if (iotHubKeyMatches) {
|
||||
iotHubKey = iotHubKeyMatches[1];
|
||||
}
|
||||
if (iotHubKeyNameMatches) {
|
||||
iotHubKeyName = iotHubKeyNameMatches[1];
|
||||
}
|
||||
|
||||
if (!iotHubName || !iotHubKeyName || !iotHubKey) {
|
||||
throw new Error('Cannot parse IoT Hub connection string.');
|
||||
}
|
||||
if (!iotHubName || !iotHubKeyName || !iotHubKey) {
|
||||
throw new Error('Cannot parse IoT Hub connection string.');
|
||||
}
|
||||
|
||||
const asaIoTHubArmTemplatePath =
|
||||
const asaIoTHubArmTemplatePath =
|
||||
this.extensionContext.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, 'arm',
|
||||
'streamanalytics-input-iothub.json'));
|
||||
const asaIoTHubArmTemplate =
|
||||
FileNames.resourcesFolderName, 'arm',
|
||||
'streamanalytics-input-iothub.json'));
|
||||
const asaIoTHubArmTemplate =
|
||||
JSON.parse(fs.readFileSync(asaIoTHubArmTemplatePath, 'utf8')) as
|
||||
ARMTemplate;
|
||||
const asaIotHubArmParameters = {
|
||||
streamAnalyticsJobName: {value: streamAnalyticsJobName},
|
||||
inputName: {value: `iothub-${componentConfig.id}`},
|
||||
iotHubName: {value: iotHubName},
|
||||
iotHubKeyName: {value: iotHubKeyName},
|
||||
iotHubKey: {value: iotHubKey}
|
||||
};
|
||||
const asaIotHubArmParameters = {
|
||||
streamAnalyticsJobName: { value: streamAnalyticsJobName },
|
||||
inputName: { value: `iothub-${componentConfig.id}` },
|
||||
iotHubName: { value: iotHubName },
|
||||
iotHubKeyName: { value: iotHubKeyName },
|
||||
iotHubKey: { value: iotHubKey }
|
||||
};
|
||||
|
||||
const asaInputDeploy = await AzureUtility.deployARMTemplate(
|
||||
asaIoTHubArmTemplate, asaIotHubArmParameters);
|
||||
if (!asaInputDeploy) {
|
||||
throw new Error('Provision Stream Analytics Job failed.');
|
||||
}
|
||||
const asaInputDeploy = await AzureUtility.deployARMTemplate(
|
||||
asaIoTHubArmTemplate, asaIotHubArmParameters);
|
||||
if (!asaInputDeploy) {
|
||||
throw new Error('Provision Stream Analytics Job failed.');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(
|
||||
`Not supported ASA input type: ${componentConfig.type}.`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(
|
||||
`Not supported ASA input type: ${componentConfig.type}.`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (componentConfig.type) {
|
||||
case 'CosmosDB': {
|
||||
if (!componentConfig.componentInfo) {
|
||||
return false;
|
||||
}
|
||||
const cosmosDBAccountName =
|
||||
case 'CosmosDB': {
|
||||
if (!componentConfig.componentInfo) {
|
||||
return false;
|
||||
}
|
||||
const cosmosDBAccountName =
|
||||
componentConfig.componentInfo.values.cosmosDBAccountName;
|
||||
const cosmosDBDatabase =
|
||||
const cosmosDBDatabase =
|
||||
componentConfig.componentInfo.values.cosmosDBDatabase;
|
||||
const cosmosDBCollection =
|
||||
const cosmosDBCollection =
|
||||
componentConfig.componentInfo.values.cosmosDBCollection;
|
||||
if (!cosmosDBAccountName || !cosmosDBDatabase ||
|
||||
if (!cosmosDBAccountName || !cosmosDBDatabase ||
|
||||
!cosmosDBCollection) {
|
||||
throw new Error('Cannot get Cosmos DB connection information.');
|
||||
}
|
||||
throw new Error('Cannot get Cosmos DB connection information.');
|
||||
}
|
||||
|
||||
const asaCosmosDBArmTemplatePath =
|
||||
const asaCosmosDBArmTemplatePath =
|
||||
this.extensionContext.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, 'arm',
|
||||
'streamanalytics-output-cosmosdb.json'));
|
||||
const asaCosmosDBArmTemplate =
|
||||
FileNames.resourcesFolderName, 'arm',
|
||||
'streamanalytics-output-cosmosdb.json'));
|
||||
const asaCosmosDBArmTemplate =
|
||||
JSON.parse(fs.readFileSync(
|
||||
asaCosmosDBArmTemplatePath, 'utf8')) as ARMTemplate;
|
||||
const asaCosmosArmParameters = {
|
||||
streamAnalyticsJobName: {value: streamAnalyticsJobName},
|
||||
outputName: {value: `cosmosdb-${componentConfig.id}`},
|
||||
cosmosDBName: {value: cosmosDBAccountName},
|
||||
cosmosDBDatabase: {value: cosmosDBDatabase},
|
||||
cosmosDBCollection: {value: cosmosDBCollection}
|
||||
};
|
||||
asaCosmosDBArmTemplatePath, 'utf8')) as ARMTemplate;
|
||||
const asaCosmosArmParameters = {
|
||||
streamAnalyticsJobName: { value: streamAnalyticsJobName },
|
||||
outputName: { value: `cosmosdb-${componentConfig.id}` },
|
||||
cosmosDBName: { value: cosmosDBAccountName },
|
||||
cosmosDBDatabase: { value: cosmosDBDatabase },
|
||||
cosmosDBCollection: { value: cosmosDBCollection }
|
||||
};
|
||||
|
||||
const asaOutputDeploy = await AzureUtility.deployARMTemplate(
|
||||
asaCosmosDBArmTemplate, asaCosmosArmParameters);
|
||||
if (!asaOutputDeploy) {
|
||||
throw new Error('Provision Stream Analytics Job failed.');
|
||||
}
|
||||
const asaOutputDeploy = await AzureUtility.deployARMTemplate(
|
||||
asaCosmosDBArmTemplate, asaCosmosArmParameters);
|
||||
if (!asaOutputDeploy) {
|
||||
throw new Error('Provision Stream Analytics Job failed.');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(
|
||||
`Not supported ASA output type: ${componentConfig.type}.`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(
|
||||
`Not supported ASA output type: ${componentConfig.type}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -372,7 +373,7 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Stream Analytics Job provision succeeded.');
|
||||
this.channel, 'Stream Analytics Job provision succeeded.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -384,7 +385,7 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
let stopPending: NodeJS.Timer|null = null;
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Stopping Stream Analytics Job...');
|
||||
this.channel, 'Stopping Stream Analytics Job...');
|
||||
stopPending = setInterval(() => {
|
||||
this.channel.append('.');
|
||||
}, 1000);
|
||||
|
@ -393,7 +394,7 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
if (!jobStopped) {
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Stop Stream Analytics Job failed.');
|
||||
this.channel, 'Stop Stream Analytics Job failed.');
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
|
@ -401,44 +402,44 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
clearInterval(stopPending);
|
||||
channelShowAndAppendLine(this.channel, '.');
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Stop Stream Analytics Job succeeded.');
|
||||
this.channel, 'Stop Stream Analytics Job succeeded.');
|
||||
}
|
||||
}
|
||||
|
||||
const resourceId = `/subscriptions/${this.subscriptionId}/resourceGroups/${
|
||||
this.resourceGroup}/providers/Microsoft.StreamAnalytics/streamingjobs/${
|
||||
this.streamAnalyticsJobName}/transformations/Transformation`;
|
||||
this.resourceGroup}/providers/Microsoft.StreamAnalytics/streamingjobs/${
|
||||
this.streamAnalyticsJobName}/transformations/Transformation`;
|
||||
const apiVersion = '2015-10-01';
|
||||
if (!fs.existsSync(this.queryPath)) {
|
||||
throw new Error(`Cannot find query file at ${this.queryPath}`);
|
||||
}
|
||||
const query = fs.readFileSync(this.queryPath, 'utf8');
|
||||
const parameters = {properties: {streamingUnits: 1, query}};
|
||||
const parameters = { properties: { streamingUnits: 1, query } };
|
||||
|
||||
let deployPending: NodeJS.Timer|null = null;
|
||||
try {
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Deploying Stream Analytics Job...');
|
||||
this.channel, 'Deploying Stream Analytics Job...');
|
||||
deployPending = setInterval(() => {
|
||||
this.channel.append('.');
|
||||
}, 1000);
|
||||
}
|
||||
const deployment = await azureClient.resources.createOrUpdateById(
|
||||
resourceId, apiVersion, parameters);
|
||||
resourceId, apiVersion, parameters);
|
||||
if (this.channel && deployPending) {
|
||||
clearInterval(deployPending);
|
||||
channelShowAndAppendLine(this.channel, '.');
|
||||
channelPrintJsonObject(this.channel, deployment);
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Stream Analytics Job query deploy succeeded.');
|
||||
this.channel, 'Stream Analytics Job query deploy succeeded.');
|
||||
}
|
||||
|
||||
// Start Job
|
||||
let startPending: NodeJS.Timer|null = null;
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Starting Stream Analytics Job...');
|
||||
this.channel, 'Starting Stream Analytics Job...');
|
||||
startPending = setInterval(() => {
|
||||
this.channel.append('.');
|
||||
}, 1000);
|
||||
|
@ -447,14 +448,14 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
if (!jobStarted) {
|
||||
if (this.channel) {
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Start Stream Analytics Job failed.');
|
||||
this.channel, 'Start Stream Analytics Job failed.');
|
||||
}
|
||||
return false;
|
||||
} else if (this.channel && startPending) {
|
||||
clearInterval(startPending);
|
||||
channelShowAndAppendLine(this.channel, '.');
|
||||
channelShowAndAppendLine(
|
||||
this.channel, 'Start Stream Analytics Job succeeded.');
|
||||
this.channel, 'Start Stream Analytics Job succeeded.');
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.channel && deployPending) {
|
||||
|
@ -466,20 +467,21 @@ export class StreamAnalyticsJob implements Component, Provisionable,
|
|||
return true;
|
||||
}
|
||||
|
||||
async stop() {
|
||||
async stop(): Promise<unknown> {
|
||||
return await this.callAction(StreamAnalyticsAction.Stop);
|
||||
}
|
||||
|
||||
async start() {
|
||||
async start(): Promise<unknown> {
|
||||
return await this.callAction(StreamAnalyticsAction.Start);
|
||||
}
|
||||
|
||||
async getState() {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async getState(): Promise<any> {
|
||||
const azureClient = this.azureClient || await this.initAzureClient();
|
||||
|
||||
const resourceId = `/subscriptions/${this.subscriptionId}/resourceGroups/${
|
||||
this.resourceGroup}/providers/Microsoft.StreamAnalytics/streamingjobs/${
|
||||
this.streamAnalyticsJobName}`;
|
||||
this.resourceGroup}/providers/Microsoft.StreamAnalytics/streamingjobs/${
|
||||
this.streamAnalyticsJobName}`;
|
||||
const apiVersion = '2015-10-01';
|
||||
const res = await azureClient.resources.getById(resourceId, apiVersion);
|
||||
return res.properties.jobState;
|
||||
|
|
|
@ -7,13 +7,13 @@ import * as vscode from 'vscode';
|
|||
import * as utils from './utils';
|
||||
import * as path from 'path';
|
||||
|
||||
import {TelemetryContext} from './telemetry';
|
||||
import {ScaffoldType, PlatformType} from './constants';
|
||||
import {RemoteExtension} from './Models/RemoteExtension';
|
||||
import {IoTWorkbenchProjectBase, OpenScenario} from './Models/IoTWorkbenchProjectBase';
|
||||
import {ProjectHostType} from './Models/Interfaces/ProjectHostType';
|
||||
import {configExternalCMakeProjectToIoTContainerProject} from './utils';
|
||||
import {CancelOperationError} from './CancelOperationError';
|
||||
import { TelemetryContext } from './telemetry';
|
||||
import { ScaffoldType, PlatformType } from './constants';
|
||||
import { RemoteExtension } from './Models/RemoteExtension';
|
||||
import { IoTWorkbenchProjectBase, OpenScenario } from './Models/IoTWorkbenchProjectBase';
|
||||
import { ProjectHostType } from './Models/Interfaces/ProjectHostType';
|
||||
import { configExternalCMakeProjectToIoTContainerProject } from './utils';
|
||||
import { CancelOperationError } from './CancelOperationError';
|
||||
|
||||
const impor = require('impor')(__dirname);
|
||||
const ioTWorkspaceProjectModule = impor('./Models/IoTWorkspaceProject') as
|
||||
|
@ -24,8 +24,8 @@ const ioTContainerizedProjectModule =
|
|||
|
||||
export class ProjectEnvironmentConfiger {
|
||||
async configureCmakeProjectEnvironment(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> {
|
||||
// Only configure project when not in remote environment
|
||||
const isLocal = RemoteExtension.checkLocalBeforeRunCommand(context);
|
||||
if (!isLocal) {
|
||||
|
@ -40,34 +40,34 @@ export class ProjectEnvironmentConfiger {
|
|||
}
|
||||
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
title: 'CMake Project development container configuration',
|
||||
location: vscode.ProgressLocation.Window,
|
||||
},
|
||||
async () => {
|
||||
await ProjectEnvironmentConfiger
|
||||
.configureProjectEnvironmentAsPlatform(
|
||||
context, channel, telemetryContext,
|
||||
PlatformType.EmbeddedLinux, projectRootPath, scaffoldType);
|
||||
{
|
||||
title: 'CMake Project development container configuration',
|
||||
location: vscode.ProgressLocation.Window,
|
||||
},
|
||||
async () => {
|
||||
await ProjectEnvironmentConfiger
|
||||
.configureProjectEnvironmentAsPlatform(
|
||||
context, channel, telemetryContext,
|
||||
PlatformType.EmbeddedLinux, projectRootPath, scaffoldType);
|
||||
|
||||
const message =
|
||||
const message =
|
||||
`Successfully configured development container for CMake project.`;
|
||||
utils.channelShowAndAppendLine(channel, message);
|
||||
vscode.window.showInformationMessage(message);
|
||||
});
|
||||
utils.channelShowAndAppendLine(channel, message);
|
||||
vscode.window.showInformationMessage(message);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static async configureProjectEnvironmentAsPlatform(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, platform: PlatformType,
|
||||
projectFileRootPath: string, scaffoldType: ScaffoldType): Promise<void> {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, platform: PlatformType,
|
||||
projectFileRootPath: string, scaffoldType: ScaffoldType): Promise<void> {
|
||||
let project;
|
||||
if (platform === PlatformType.Arduino) {
|
||||
// Verify it is an iot workbench Arduino project
|
||||
const projectHostType = await IoTWorkbenchProjectBase.getProjectType(
|
||||
scaffoldType, projectFileRootPath);
|
||||
scaffoldType, projectFileRootPath);
|
||||
if (projectHostType !== ProjectHostType.Workspace) {
|
||||
const message =
|
||||
`This is not an iot workbench Arduino project. You cannot configure it as Arduino platform.`;
|
||||
|
@ -77,7 +77,7 @@ export class ProjectEnvironmentConfiger {
|
|||
|
||||
const projectRootPath = path.join(projectFileRootPath, '..');
|
||||
project = new ioTWorkspaceProjectModule.IoTWorkspaceProject(
|
||||
context, channel, telemetryContext, projectRootPath);
|
||||
context, channel, telemetryContext, projectRootPath);
|
||||
if (!project) {
|
||||
// Ensure the project is correctly open.
|
||||
await utils.properlyOpenIoTWorkspaceProject(telemetryContext);
|
||||
|
@ -90,7 +90,7 @@ export class ProjectEnvironmentConfiger {
|
|||
await RemoteExtension.checkRemoteExtension();
|
||||
|
||||
project = new ioTContainerizedProjectModule.IoTContainerizedProject(
|
||||
context, channel, telemetryContext, projectFileRootPath);
|
||||
context, channel, telemetryContext, projectFileRootPath);
|
||||
} else {
|
||||
throw new Error('unsupported platform');
|
||||
}
|
||||
|
@ -99,9 +99,9 @@ export class ProjectEnvironmentConfiger {
|
|||
|
||||
// Add configuration files
|
||||
await project.configureProjectEnvironmentCore(
|
||||
projectFileRootPath, scaffoldType);
|
||||
projectFileRootPath, scaffoldType);
|
||||
|
||||
await project.openProject(
|
||||
scaffoldType, false, OpenScenario.configureProject);
|
||||
scaffoldType, false, OpenScenario.configureProject);
|
||||
}
|
||||
}
|
|
@ -4,11 +4,11 @@ import * as vscode from 'vscode';
|
|||
import * as fs from 'fs';
|
||||
|
||||
export class WorkbenchExtension {
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private static extension: vscode.Extension<any>|undefined;
|
||||
|
||||
static getExtension(context: vscode.ExtensionContext):
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
vscode.Extension<any>|undefined {
|
||||
if (!WorkbenchExtension.extension) {
|
||||
const extensionId = WorkbenchExtension.getExtensionId(context);
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
'use strict';
|
||||
import {FileNames} from './constants';
|
||||
import {Board} from './Models/Interfaces/Board';
|
||||
import { FileNames } from './constants';
|
||||
import { Board } from './Models/Interfaces/Board';
|
||||
import * as path from 'path';
|
||||
|
||||
interface BoardList {
|
||||
|
@ -25,14 +25,14 @@ export class BoardProvider {
|
|||
this.boardFolderPath = boardFolderPath;
|
||||
}
|
||||
|
||||
get list() {
|
||||
get list(): Board[] {
|
||||
const boardList =
|
||||
path.join(this.boardFolderPath, FileNames.boardListFileName);
|
||||
const boardsJson: BoardList = require(boardList);
|
||||
return boardsJson.boards;
|
||||
}
|
||||
|
||||
find(option: BoardOption) {
|
||||
find(option: BoardOption): Board|undefined {
|
||||
const list = this.list;
|
||||
return list.find(board => {
|
||||
for (const key of Object.keys(option)) {
|
||||
|
@ -49,8 +49,8 @@ export class BoardProvider {
|
|||
|
||||
if (key === 'vendorId' || key === 'productId') {
|
||||
const optionId = typeof optionProperty.value === 'number' ?
|
||||
optionProperty.value :
|
||||
Number(`0x${optionProperty.value}`);
|
||||
optionProperty.value :
|
||||
Number(`0x${optionProperty.value}`);
|
||||
const boardId = Number(`0x${boardProperty.value}`);
|
||||
if (optionId !== boardId) {
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { InternalError } from './InternalError';
|
||||
|
||||
export class BoardNotFoundError extends InternalError {
|
||||
constructor (board: string) {
|
||||
const errorMessage = `${board} is not found in board list.`;
|
||||
super(errorMessage);
|
||||
this.name = 'BoardNotFoundError';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
import { ExtensionName } from '../../Models/Interfaces/Api';
|
||||
|
||||
// User Error
|
||||
/**
|
||||
* Error class used when user cancel operation.
|
||||
*/
|
||||
export class OperationCanceledError extends Error {
|
||||
constructor (message: string) {
|
||||
super(`Operation cancelled: ${message}`);
|
||||
this.name = 'CancelOperationError';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error class used when remote environment does not support a operation.
|
||||
*/
|
||||
export class RemoteEnvNotSupportedError extends Error {
|
||||
/**
|
||||
* Construct a remote environemt not supported error.
|
||||
* @param suggestedOperation message of the recommended operation for user
|
||||
*/
|
||||
constructor (suggestedOperation: string) {
|
||||
super(`The operation is not supported to be run in remote environment. ${
|
||||
suggestedOperation}`);
|
||||
this.name = 'RemoteEnvNotSupportedError';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error class used when resource (file, directory, etc) is not found
|
||||
*/
|
||||
export class ResourceNotFoundError extends Error {
|
||||
/**
|
||||
* Construct a resource not found error.
|
||||
* @param resource The name of resource that is missing
|
||||
* @param suggestedOperation Recommended operation for user.
|
||||
*/
|
||||
constructor (
|
||||
operation: string, resource: string, suggestedOperation?: string) {
|
||||
super(`Failed to ${operation}: Unable to find ${resource}. ${
|
||||
suggestedOperation}`);
|
||||
this.name = 'ResourceNotFoundError';
|
||||
}
|
||||
}
|
||||
|
||||
export class DependentExtensionNotFoundError extends Error {
|
||||
constructor (extension: ExtensionName) {
|
||||
super(`Dependent extension ${
|
||||
extension} is not found. Please install it from Marketplace.`);
|
||||
this.name = 'DependentExtensionNotFound';
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkspaceNotOpenError extends Error {
|
||||
constructor () {
|
||||
super(
|
||||
'You have not yet opened a folder in Visual Studio Code. Please select a folder first.');
|
||||
this.name = 'WorkspaceNotOpenError';
|
||||
}
|
||||
}
|
||||
|
||||
export class PrerequisiteNotMetError extends Error {
|
||||
constructor (operation: string, suggestedOperation?: string) {
|
||||
super(`Failed to ${operation} because prerequisite is not met. ${
|
||||
suggestedOperation}`);
|
||||
this.name = 'PrerequisiteNotMetError';
|
||||
}
|
||||
}
|
||||
|
||||
// System Error
|
||||
export class OperationFailedError extends Error {
|
||||
constructor (operation: string, suggestedOperation?: string) {
|
||||
super(`Failed to ${operation}. ${suggestedOperation}`);
|
||||
this.name = 'OperationFailedError';
|
||||
}
|
||||
}
|
||||
|
||||
export class BoardNotFoundError extends Error {
|
||||
constructor (board: string) {
|
||||
super(`${board} is not found in board list.`);
|
||||
this.name = 'BoardNotFoundError';
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigNotFoundError extends Error {
|
||||
constructor (configKey: string, suggestedOperation?: string) {
|
||||
super(`Failed to get ${configKey} from workspace settings. ${
|
||||
suggestedOperation}`);
|
||||
this.name = 'ConfigNotFoundError';
|
||||
}
|
||||
}
|
||||
|
||||
export class TypeNotSupportedError extends Error {
|
||||
constructor (typeName: string, typeValue: string) {
|
||||
super(`Unsupported ${typeName}: ${typeValue}`);
|
||||
this.name = 'TypeNotSupportedError';
|
||||
}
|
||||
}
|
||||
|
||||
export class InternalError extends Error {
|
||||
constructor (message: string) {
|
||||
super(`Internal Error: ${message}.`);
|
||||
this.name = 'InternalError';
|
||||
}
|
||||
}
|
||||
|
||||
export class ArgumentEmptyOrNullError extends Error {
|
||||
constructor (argument: string, suggestedOperation?: string) {
|
||||
super(`Argument ${argument} is empty or null. ${suggestedOperation}`);
|
||||
this.name = 'ArgumentEmptyOrNullError';
|
||||
}
|
||||
}
|
||||
|
||||
export class ArgumentInvalidError extends Error {
|
||||
constructor (argument: string, suggestedOperation?: string) {
|
||||
super(`${argument} is invalid. ${suggestedOperation}`);
|
||||
this.name = 'ArgumentInvalidError';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
export class InternalError extends Error {
|
||||
constructor (errorMessage: string) {
|
||||
super(`Internal Error: ${errorMessage}.`);
|
||||
this.name = 'InternalError';
|
||||
}
|
||||
}
|
||||
/*
|
||||
- BoardNotFoundError
|
||||
- TypeNotSupportedError
|
||||
*/
|
||||
|
||||
export class UserError extends Error {
|
||||
constructor (operation: string, suggestedOperation?: string) {
|
||||
super(`Failed to ${operation}. ${suggestedOperation}`);
|
||||
this.name = 'UserError';
|
||||
}
|
||||
}
|
||||
|
||||
export class OperationCanceledError extends UserError {
|
||||
constructor (message: string) {
|
||||
super(`Operation cancelled: ${message}`);
|
||||
this.name = 'CancelOperationError';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import { InternalError } from './InternalError';
|
||||
|
||||
export class TypeNotSupportedError extends InternalError {
|
||||
constructor (typeName: string, typeValue: string) {
|
||||
super(`Unsupported ${typeName}: ${typeValue}`);
|
||||
this.name = 'TypeNotSupportedError';
|
||||
}
|
||||
}
|
|
@ -5,16 +5,16 @@ import * as vscode from 'vscode';
|
|||
|
||||
export class ConfigHandler {
|
||||
static async update(
|
||||
key: string, value: {}, target = vscode.ConfigurationTarget.Workspace) {
|
||||
key: string, value: {}, target = vscode.ConfigurationTarget.Workspace): Promise<void> {
|
||||
if (!key) {
|
||||
throw new Error('Key is empty.');
|
||||
}
|
||||
|
||||
return await vscode.workspace.getConfiguration('IoTWorkbench')
|
||||
.update(key, value, target);
|
||||
.update(key, value, target);
|
||||
}
|
||||
|
||||
static get<T>(key: string) {
|
||||
static get<T>(key: string): T|undefined {
|
||||
if (!key) {
|
||||
throw new Error('Key is empty.');
|
||||
}
|
||||
|
|
|
@ -7,17 +7,17 @@ import * as fs from 'fs-plus';
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as AdmZip from 'adm-zip';
|
||||
import {IoTWorkbenchSettings} from './IoTSettings';
|
||||
import { IoTWorkbenchSettings } from './IoTSettings';
|
||||
import * as utils from './utils';
|
||||
import {Board, BoardQuickPickItem} from './Models/Interfaces/Board';
|
||||
import {TelemetryContext} from './telemetry';
|
||||
import {FileNames} from './constants';
|
||||
import {ArduinoPackageManager} from './ArduinoPackageManager';
|
||||
import {BoardProvider} from './boardProvider';
|
||||
import {VSCExpress} from 'vscode-express';
|
||||
import {RemoteExtension} from './Models/RemoteExtension';
|
||||
import {CancelOperationError} from './CancelOperationError';
|
||||
import {IoTCubeCommands} from './common/Commands';
|
||||
import { Board, BoardQuickPickItem } from './Models/Interfaces/Board';
|
||||
import { TelemetryContext } from './telemetry';
|
||||
import { FileNames } from './constants';
|
||||
import { ArduinoPackageManager } from './ArduinoPackageManager';
|
||||
import { BoardProvider } from './boardProvider';
|
||||
import { VSCExpress } from 'vscode-express';
|
||||
import { RemoteExtension } from './Models/RemoteExtension';
|
||||
import { CancelOperationError } from './CancelOperationError';
|
||||
import { IoTCubeCommands } from './common/Commands';
|
||||
|
||||
type OptionsWithUri = import('request-promise').OptionsWithUri;
|
||||
|
||||
|
@ -31,7 +31,7 @@ export class ExampleExplorer {
|
|||
|
||||
private static _vscexpress: VSCExpress|undefined;
|
||||
|
||||
private async moveTempFiles(fsPath: string) {
|
||||
private async moveTempFiles(fsPath: string): Promise<boolean> {
|
||||
const tempPath = path.join(fsPath, '.temp');
|
||||
const tempPathList = fs.listSync(tempPath);
|
||||
let examplePath: string|undefined = undefined;
|
||||
|
@ -50,7 +50,7 @@ export class ExampleExplorer {
|
|||
examplePathList.forEach(item => {
|
||||
if (item !== '.' && item !== '..') {
|
||||
fs.moveSync(
|
||||
path.join(examplePath as string, item), path.join(fsPath, item));
|
||||
path.join(examplePath as string, item), path.join(fsPath, item));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -58,9 +58,7 @@ export class ExampleExplorer {
|
|||
return true;
|
||||
}
|
||||
|
||||
private async downloadExamplePackage(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
url: string, fsPath: string): Promise<boolean> {
|
||||
private async downloadExamplePackage(channel: vscode.OutputChannel, url: string, fsPath: string): Promise<boolean> {
|
||||
const loading = setInterval(() => {
|
||||
channel.append('.');
|
||||
}, 1000);
|
||||
|
@ -89,7 +87,7 @@ export class ExampleExplorer {
|
|||
}
|
||||
}
|
||||
|
||||
private async generateExampleFolder(exampleName: string) {
|
||||
private async generateExampleFolder(exampleName: string): Promise<string> {
|
||||
const settings = await IoTWorkbenchSettings.getInstance();
|
||||
const workbench = settings.getWorkbenchPath();
|
||||
|
||||
|
@ -109,24 +107,24 @@ export class ExampleExplorer {
|
|||
const workspaceFile = workspaceFiles[0]; // just pick the first one
|
||||
if (fs.existsSync(workspaceFile)) {
|
||||
const selection = await vscode.window.showQuickPick(
|
||||
[
|
||||
{
|
||||
label: `Open an existing example`,
|
||||
description: '',
|
||||
detail: `Example exists: ${name}`
|
||||
},
|
||||
{
|
||||
label: 'Generate a new example',
|
||||
description: '',
|
||||
detail: 'Create a new folder to generate the example'
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
ignoreFocusOut: true,
|
||||
matchOnDescription: true,
|
||||
matchOnDetail: true,
|
||||
placeHolder: 'Select an option',
|
||||
});
|
||||
label: `Open an existing example`,
|
||||
description: '',
|
||||
detail: `Example exists: ${name}`
|
||||
},
|
||||
{
|
||||
label: 'Generate a new example',
|
||||
description: '',
|
||||
detail: 'Create a new folder to generate the example'
|
||||
}
|
||||
],
|
||||
{
|
||||
ignoreFocusOut: true,
|
||||
matchOnDescription: true,
|
||||
matchOnDetail: true,
|
||||
placeHolder: 'Select an option',
|
||||
});
|
||||
|
||||
if (!selection) {
|
||||
return '';
|
||||
|
@ -148,7 +146,7 @@ export class ExampleExplorer {
|
|||
const name = path.join(workbench, 'examples', exampleName);
|
||||
if (!utils.fileExistsSync(name) && !utils.directoryExistsSync(name)) {
|
||||
if (!/^([a-z0-9_]|[a-z0-9_][-a-z0-9_.]*[a-z0-9_])$/i.test(
|
||||
exampleName)) {
|
||||
exampleName)) {
|
||||
return 'Folder name can only contain letters, numbers, "-" and ".", and cannot start or end with "-" or ".".';
|
||||
}
|
||||
return;
|
||||
|
@ -175,16 +173,14 @@ export class ExampleExplorer {
|
|||
return customizedPath;
|
||||
}
|
||||
|
||||
async selectBoard(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
async selectBoard(context: vscode.ExtensionContext, telemetryContext: TelemetryContext): Promise<void> {
|
||||
const isLocal = RemoteExtension.checkLocalBeforeRunCommand(context);
|
||||
if (!isLocal) {
|
||||
return;
|
||||
}
|
||||
|
||||
const boardFolderPath = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
const boardProvider = new BoardProvider(boardFolderPath);
|
||||
const boardItemList: BoardQuickPickItem[] = [];
|
||||
const boards = boardProvider.list.filter(board => board.exampleUrl);
|
||||
|
@ -221,7 +217,7 @@ export class ExampleExplorer {
|
|||
return;
|
||||
} else {
|
||||
telemetryContext.properties.board = boardSelection.label;
|
||||
const board = boardProvider.find({id: boardSelection.id});
|
||||
const board = boardProvider.find({ id: boardSelection.id });
|
||||
|
||||
if (board) {
|
||||
// To avoid block example gallery, use async to install board here
|
||||
|
@ -232,28 +228,25 @@ export class ExampleExplorer {
|
|||
ExampleExplorer._vscexpress =
|
||||
ExampleExplorer._vscexpress || new VSCExpress(context, 'views');
|
||||
ExampleExplorer._vscexpress.open(
|
||||
exampleUrl,
|
||||
board.examplePageName + ' samples - Azure IoT Device Workbench',
|
||||
vscode.ViewColumn.One, {
|
||||
enableScripts: true,
|
||||
enableCommandUris: true,
|
||||
retainContextWhenHidden: true
|
||||
});
|
||||
exampleUrl,
|
||||
board.examplePageName + ' samples - Azure IoT Device Workbench',
|
||||
vscode.ViewColumn.One, {
|
||||
enableScripts: true,
|
||||
enableCommandUris: true,
|
||||
retainContextWhenHidden: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async initializeExample(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, name?: string, url?: string,
|
||||
boardId?: string) {
|
||||
async initializeExample(context: vscode.ExtensionContext, channel: vscode.OutputChannel, telemetryContext: TelemetryContext, name?: string, url?: string, boardId?: string): Promise<void> {
|
||||
if (name && url && boardId) {
|
||||
this._exampleName = name;
|
||||
this._exampleUrl = url;
|
||||
this._boardId = boardId;
|
||||
}
|
||||
const res = await this.initializeExampleInternal(
|
||||
context, channel, telemetryContext);
|
||||
context, channel, telemetryContext);
|
||||
|
||||
if (!res) {
|
||||
throw new CancelOperationError(`Example load cancelled.`);
|
||||
|
@ -262,22 +255,22 @@ export class ExampleExplorer {
|
|||
vscode.window.showInformationMessage('Example load successfully.');
|
||||
}
|
||||
|
||||
setSelectedExample(name: string, url: string, boardId: string) {
|
||||
setSelectedExample(name: string, url: string, boardId: string): void {
|
||||
this._exampleName = name;
|
||||
this._exampleUrl = url;
|
||||
this._boardId = boardId;
|
||||
}
|
||||
|
||||
private async initializeExampleInternal(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<boolean> {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<boolean> {
|
||||
if (!this._exampleName || !this._exampleUrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const boardList = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.boardListFileName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.boardListFileName));
|
||||
const boardsJson: {boards: Board[]} = require(boardList);
|
||||
|
||||
telemetryContext.properties.Example = this._exampleName;
|
||||
|
@ -294,13 +287,13 @@ export class ExampleExplorer {
|
|||
const items = fs.listSync(fsPath, [FileNames.workspaceExtensionName]);
|
||||
if (items.length !== 0) {
|
||||
await vscode.commands.executeCommand(
|
||||
IoTCubeCommands.OpenLocally, items[0], true);
|
||||
IoTCubeCommands.OpenLocally, items[0], true);
|
||||
return true;
|
||||
}
|
||||
|
||||
utils.channelShowAndAppendLine(channel, 'Downloading example package...');
|
||||
const res =
|
||||
await this.downloadExamplePackage(context, channel, url, fsPath);
|
||||
await this.downloadExamplePackage(channel, url, fsPath);
|
||||
if (res) {
|
||||
// Follow the same pattern in Arduino extension to open examples in new
|
||||
// VSCode instance
|
||||
|
@ -308,16 +301,16 @@ export class ExampleExplorer {
|
|||
fs.listSync(fsPath, [FileNames.workspaceExtensionName]);
|
||||
if (workspaceFiles && workspaceFiles.length > 0) {
|
||||
await vscode.commands.executeCommand(
|
||||
IoTCubeCommands.OpenLocally, workspaceFiles[0], true);
|
||||
IoTCubeCommands.OpenLocally, workspaceFiles[0], true);
|
||||
return true;
|
||||
} else {
|
||||
// TODO: Add buttom to submit issue to iot-workbench repo.
|
||||
throw new Error(
|
||||
'The example does not contain a project for Azure IoT Device Workbench.');
|
||||
'The example does not contain a project for Azure IoT Device Workbench.');
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'Downloading example package failed. Please check your network settings.');
|
||||
'Downloading example package failed. Please check your network settings.');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,8 +20,8 @@ export class ExceptionHelper {
|
|||
channel: vscode.OutputChannel|undefined, errorMsg: string,
|
||||
isPopupErrorMsg: boolean): void;
|
||||
static logError(
|
||||
channel: vscode.OutputChannel|undefined, errorValue: string|Error,
|
||||
popupValue: string|boolean): void {
|
||||
channel: vscode.OutputChannel|undefined, errorValue: string|Error,
|
||||
popupValue: string|boolean): void {
|
||||
let _error: Error;
|
||||
let _message: string;
|
||||
|
||||
|
@ -43,9 +43,9 @@ export class ExceptionHelper {
|
|||
let errorMessage: string;
|
||||
if (_error.message) {
|
||||
errorMessage = _error.message;
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} else if ((_error as any).body && (_error as any).body.message) {
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
errorMessage = (_error as any).body.message;
|
||||
} else {
|
||||
errorMessage = _error.toString();
|
||||
|
|
908
src/extension.ts
908
src/extension.ts
|
@ -6,45 +6,348 @@
|
|||
// Import the module and reference it with the alias vscode in your code below
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import {BoardProvider} from './boardProvider';
|
||||
import {ProjectInitializer} from './projectInitializer';
|
||||
import {DeviceOperator} from './DeviceOperator';
|
||||
import {AzureOperator} from './AzureOperator';
|
||||
import {IoTWorkbenchSettings} from './IoTSettings';
|
||||
import {ConfigHandler} from './configHandler';
|
||||
import {CodeGeneratorCore} from './DigitalTwin/CodeGeneratorCore';
|
||||
import {ConfigKey, EventNames, FileNames} from './constants';
|
||||
import {TelemetryContext, TelemetryWorker, TelemetryResult} from './telemetry';
|
||||
import {RemoteExtension} from './Models/RemoteExtension';
|
||||
import {constructAndLoadIoTProject} from './utils';
|
||||
import {ProjectEnvironmentConfiger} from './ProjectEnvironmentConfiger';
|
||||
import {WorkbenchExtension} from './WorkbenchExtension';
|
||||
import {WorkbenchCommands, VscodeCommands} from './common/Commands';
|
||||
import {ColorizedChannel} from './DigitalTwin/pnp/src/common/colorizedChannel';
|
||||
import {Constants} from './DigitalTwin/pnp/src/common/constants';
|
||||
import {DeviceModelManager, ModelType} from './DigitalTwin/pnp/src/deviceModel/deviceModelManager';
|
||||
import {ModelRepositoryManager} from './DigitalTwin/pnp/src/modelRepository/modelRepositoryManager';
|
||||
import {IntelliSenseUtility} from './DigitalTwin/pnp/src/intelliSense/intelliSenseUtility';
|
||||
import {DigitalTwinCompletionItemProvider} from './DigitalTwin/pnp/src/intelliSense/digitalTwinCompletionItemProvider';
|
||||
import {DigitalTwinHoverProvider} from './DigitalTwin/pnp/src/intelliSense/digitalTwinHoverProvider';
|
||||
import {DigitalTwinDiagnosticProvider} from './DigitalTwin/pnp/src/intelliSense/digitalTwinDiagnosticProvider';
|
||||
import {Command} from './DigitalTwin/pnp/src/common/command';
|
||||
import {UserCancelledError} from './DigitalTwin/pnp/src/common/userCancelledError';
|
||||
import {UI, MessageType} from './DigitalTwin/pnp/src/view/ui';
|
||||
import {ProcessError} from './DigitalTwin/pnp/src/common/processError';
|
||||
import {SearchResult} from './DigitalTwin/pnp/src/modelRepository/modelRepositoryInterface';
|
||||
import {NSAT} from './nsat';
|
||||
import {DigitalTwinUtility} from './DigitalTwin/DigitalTwinUtility';
|
||||
import { BoardProvider } from './boardProvider';
|
||||
import { ProjectInitializer } from './projectInitializer';
|
||||
import { DeviceOperator } from './DeviceOperator';
|
||||
import { AzureOperator } from './AzureOperator';
|
||||
import { IoTWorkbenchSettings } from './IoTSettings';
|
||||
import { ConfigHandler } from './configHandler';
|
||||
import { CodeGeneratorCore } from './DigitalTwin/CodeGeneratorCore';
|
||||
import { ConfigKey, EventNames, FileNames } from './constants';
|
||||
import { TelemetryContext, TelemetryWorker, TelemetryResult } from './telemetry';
|
||||
import { RemoteExtension } from './Models/RemoteExtension';
|
||||
import { constructAndLoadIoTProject } from './utils';
|
||||
import { ProjectEnvironmentConfiger } from './ProjectEnvironmentConfiger';
|
||||
import { WorkbenchExtension } from './WorkbenchExtension';
|
||||
import { WorkbenchCommands, VscodeCommands } from './common/Commands';
|
||||
import { ColorizedChannel } from './DigitalTwin/pnp/src/common/colorizedChannel';
|
||||
import { Constants } from './DigitalTwin/pnp/src/common/constants';
|
||||
import { DeviceModelManager, ModelType } from './DigitalTwin/pnp/src/deviceModel/deviceModelManager';
|
||||
import { ModelRepositoryManager } from './DigitalTwin/pnp/src/modelRepository/modelRepositoryManager';
|
||||
import { IntelliSenseUtility } from './DigitalTwin/pnp/src/intelliSense/intelliSenseUtility';
|
||||
import { DigitalTwinCompletionItemProvider } from './DigitalTwin/pnp/src/intelliSense/digitalTwinCompletionItemProvider';
|
||||
import { DigitalTwinHoverProvider } from './DigitalTwin/pnp/src/intelliSense/digitalTwinHoverProvider';
|
||||
import { DigitalTwinDiagnosticProvider } from './DigitalTwin/pnp/src/intelliSense/digitalTwinDiagnosticProvider';
|
||||
import { Command } from './DigitalTwin/pnp/src/common/command';
|
||||
import { UserCancelledError } from './DigitalTwin/pnp/src/common/userCancelledError';
|
||||
import { UI, MessageType } from './DigitalTwin/pnp/src/view/ui';
|
||||
import { ProcessError } from './DigitalTwin/pnp/src/common/processError';
|
||||
import { SearchResult } from './DigitalTwin/pnp/src/modelRepository/modelRepositoryInterface';
|
||||
import { NSAT } from './nsat';
|
||||
import { DigitalTwinUtility } from './DigitalTwin/DigitalTwinUtility';
|
||||
|
||||
const impor = require('impor')(__dirname);
|
||||
const exampleExplorerModule =
|
||||
impor('./exampleExplorer') as typeof import('./exampleExplorer');
|
||||
const request = impor('request-promise') as typeof import('request-promise');
|
||||
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let telemetryWorker: any = undefined;
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
function printHello(context: vscode.ExtensionContext): void {
|
||||
const extension = WorkbenchExtension.getExtension(context);
|
||||
if (!extension) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extensionId = extension.id;
|
||||
console.log(`Congratulations, your extension ${extensionId} is now active!`);
|
||||
}
|
||||
|
||||
|
||||
function initCommandWithTelemetry(
|
||||
context: vscode.ExtensionContext, telemetryWorker: TelemetryWorker,
|
||||
outputChannel: vscode.OutputChannel, command: WorkbenchCommands,
|
||||
eventName: string, enableSurvey: boolean,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
callback: (
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
telemetrycontext: TelemetryContext, ...args: any[]) => any,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
additionalProperties?: {[key: string]: string}): void {
|
||||
context.subscriptions.push(vscode.commands.registerCommand(
|
||||
command,
|
||||
async (...commandArgs) => telemetryWorker.callCommandWithTelemetry(
|
||||
context, outputChannel, eventName, enableSurvey, callback,
|
||||
additionalProperties, ...commandArgs)));
|
||||
}
|
||||
|
||||
function initCommand(
|
||||
context: vscode.ExtensionContext, command: WorkbenchCommands,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
callback: (...args: any[]) => Promise<any>): void {
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand(command, callback));
|
||||
}
|
||||
|
||||
function initIntelliSense(context: vscode.ExtensionContext): void {
|
||||
// init DigitalTwin graph
|
||||
IntelliSenseUtility.initGraph(context);
|
||||
// register providers of completionItem and hover
|
||||
const selector: vscode.DocumentSelector = {
|
||||
language: 'json',
|
||||
scheme: 'file',
|
||||
};
|
||||
context.subscriptions.push(
|
||||
vscode.languages.registerCompletionItemProvider(
|
||||
selector,
|
||||
new DigitalTwinCompletionItemProvider(),
|
||||
Constants.COMPLETION_TRIGGER,
|
||||
),
|
||||
);
|
||||
context.subscriptions.push(vscode.languages.registerHoverProvider(
|
||||
selector, new DigitalTwinHoverProvider()));
|
||||
// register diagnostic
|
||||
let pendingDiagnostic: NodeJS.Timer;
|
||||
const diagnosticCollection: vscode.DiagnosticCollection =
|
||||
vscode.languages.createDiagnosticCollection(
|
||||
Constants.CHANNEL_NAME,
|
||||
);
|
||||
const diagnosticProvider = new DigitalTwinDiagnosticProvider();
|
||||
const activeTextEditor: vscode.TextEditor|undefined =
|
||||
vscode.window.activeTextEditor;
|
||||
if (activeTextEditor) {
|
||||
diagnosticProvider.updateDiagnostics(
|
||||
activeTextEditor.document, diagnosticCollection);
|
||||
}
|
||||
context.subscriptions.push(diagnosticCollection);
|
||||
context.subscriptions.push(
|
||||
vscode.window.onDidChangeActiveTextEditor((event) => {
|
||||
if (event) {
|
||||
diagnosticProvider.updateDiagnostics(
|
||||
event.document, diagnosticCollection);
|
||||
}
|
||||
}),
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidChangeTextDocument((event) => {
|
||||
if (event) {
|
||||
if (pendingDiagnostic) {
|
||||
clearTimeout(pendingDiagnostic);
|
||||
}
|
||||
pendingDiagnostic = setTimeout(
|
||||
() => diagnosticProvider.updateDiagnostics(
|
||||
event.document, diagnosticCollection),
|
||||
Constants.DEFAULT_TIMER_MS,
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidCloseTextDocument(
|
||||
(document) => diagnosticCollection.delete(document.uri)),
|
||||
);
|
||||
}
|
||||
|
||||
function initDigitalTwinCommand(
|
||||
context: vscode.ExtensionContext,
|
||||
telemetryWorker: TelemetryWorker,
|
||||
outputChannel: ColorizedChannel,
|
||||
enableSurvey: boolean,
|
||||
command: Command,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
callback: (telemetryContext: TelemetryContext, ...args: any[]) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Promise<any>,
|
||||
): void {
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
command,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async (...args: any[]) => {
|
||||
const start: number = Date.now();
|
||||
const telemetryContext: TelemetryContext =
|
||||
telemetryWorker.createContext();
|
||||
try {
|
||||
return await callback(telemetryContext, ...args);
|
||||
} catch (error) {
|
||||
telemetryContext.properties.error = error.name;
|
||||
telemetryContext.properties.errorMessage = error.message;
|
||||
if (error instanceof UserCancelledError) {
|
||||
telemetryContext.properties.result = TelemetryResult.Cancelled;
|
||||
outputChannel.warn(error.message);
|
||||
} else {
|
||||
telemetryContext.properties.result = TelemetryResult.Failed;
|
||||
UI.showNotification(MessageType.Error, error.message);
|
||||
if (error instanceof ProcessError) {
|
||||
const message = `${error.message}\n${error.stack}`;
|
||||
outputChannel.error(message, error.component);
|
||||
} else {
|
||||
outputChannel.error(error.message);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
telemetryContext.measurements.duration =
|
||||
(Date.now() - start) / 1000;
|
||||
telemetryWorker.sendEvent(command, telemetryContext);
|
||||
outputChannel.show();
|
||||
if (enableSurvey) {
|
||||
NSAT.takeSurvey(context);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
// DigitalTwin extension part
|
||||
function initDigitalTwin(
|
||||
context: vscode.ExtensionContext,
|
||||
outputChannel: vscode.OutputChannel): void {
|
||||
const colorizedChannel = new ColorizedChannel(Constants.CHANNEL_NAME);
|
||||
context.subscriptions.push(colorizedChannel);
|
||||
const deviceModelManager = new DeviceModelManager(context, colorizedChannel);
|
||||
const modelRepositoryManager = new ModelRepositoryManager(
|
||||
context, Constants.WEB_VIEW_PATH, colorizedChannel);
|
||||
|
||||
DigitalTwinUtility.init(modelRepositoryManager, outputChannel);
|
||||
initIntelliSense(context);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.CreateInterface,
|
||||
async():
|
||||
Promise<void> => {
|
||||
return deviceModelManager.createModel(ModelType.Interface);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.CreateCapabilityModel,
|
||||
async():
|
||||
Promise<void> => {
|
||||
return deviceModelManager.createModel(ModelType.CapabilityModel);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.OpenRepository,
|
||||
async():
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.signIn();
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.SignOutRepository,
|
||||
async():
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.signOut();
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.SubmitFiles,
|
||||
async(telemetryContext: TelemetryContext):
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.submitFiles(telemetryContext);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
false,
|
||||
Command.DeleteModels,
|
||||
async(
|
||||
_telemetryContext: TelemetryContext, publicRepository: boolean,
|
||||
modelIds: string[]):
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.deleteModels(
|
||||
publicRepository, modelIds);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
false,
|
||||
Command.DownloadModels,
|
||||
async(
|
||||
_telemetryContext: TelemetryContext, publicRepository: boolean,
|
||||
modelIds: string[]):
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.downloadModels(
|
||||
publicRepository, modelIds);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
false,
|
||||
Command.SearchInterface,
|
||||
async(
|
||||
_telemetryContext: TelemetryContext,
|
||||
publicRepository: boolean,
|
||||
keyword?: string,
|
||||
pageSize?: number,
|
||||
continuationToken?: string,
|
||||
):
|
||||
Promise<SearchResult> => {
|
||||
return modelRepositoryManager.searchModel(
|
||||
ModelType.Interface,
|
||||
publicRepository,
|
||||
keyword,
|
||||
pageSize,
|
||||
continuationToken,
|
||||
);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
false,
|
||||
Command.SearchCapabilityModel,
|
||||
async(
|
||||
_telemetryContext: TelemetryContext,
|
||||
publicRepository: boolean,
|
||||
keyword?: string,
|
||||
pageSize?: number,
|
||||
continuationToken?: string,
|
||||
):
|
||||
Promise<SearchResult> => {
|
||||
return modelRepositoryManager.searchModel(
|
||||
ModelType.CapabilityModel,
|
||||
publicRepository,
|
||||
keyword,
|
||||
pageSize,
|
||||
continuationToken,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function enableUsbDetector(
|
||||
context: vscode.ExtensionContext,
|
||||
outputChannel: vscode.OutputChannel): void {
|
||||
if (RemoteExtension.isRemote(context)) {
|
||||
return;
|
||||
}
|
||||
// delay to detect usb
|
||||
const usbDetectorModule =
|
||||
impor('./usbDetector') as typeof import('./usbDetector');
|
||||
|
||||
const usbDetector = new usbDetectorModule.UsbDetector(context, outputChannel);
|
||||
usbDetector.startListening(context);
|
||||
}
|
||||
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||
printHello(context);
|
||||
|
||||
const channelName = 'Azure IoT Device Workbench';
|
||||
|
@ -57,160 +360,162 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
// project open since no command has been triggered yet.
|
||||
const telemetryContext = telemetryWorker.createContext();
|
||||
await constructAndLoadIoTProject(
|
||||
context, outputChannel, telemetryContext, true);
|
||||
context, outputChannel, telemetryContext, true);
|
||||
|
||||
const deviceOperator = new DeviceOperator();
|
||||
const azureOperator = new AzureOperator();
|
||||
const exampleExplorer = new exampleExplorerModule.ExampleExplorer();
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.InitializeProject, EventNames.createNewProjectEvent,
|
||||
true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
const projectInitializer = new ProjectInitializer();
|
||||
return projectInitializer.InitializeProject(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.InitializeProject, EventNames.createNewProjectEvent,
|
||||
true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
const projectInitializer = new ProjectInitializer();
|
||||
return projectInitializer.InitializeProject(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.ConfigureProjectEnvironment,
|
||||
EventNames.configProjectEnvironmentEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
const projectEnvConfiger = new ProjectEnvironmentConfiger();
|
||||
return projectEnvConfiger.configureCmakeProjectEnvironment(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.ConfigureProjectEnvironment,
|
||||
EventNames.configProjectEnvironmentEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
const projectEnvConfiger = new ProjectEnvironmentConfiger();
|
||||
return projectEnvConfiger.configureCmakeProjectEnvironment(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.AzureProvision,
|
||||
EventNames.azureProvisionEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return azureOperator.provision(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.AzureProvision,
|
||||
EventNames.azureProvisionEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return azureOperator.provision(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.AzureDeploy,
|
||||
EventNames.azureDeployEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return azureOperator.deploy(context, outputChannel, telemetryContext);
|
||||
});
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.AzureDeploy,
|
||||
EventNames.azureDeployEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return azureOperator.deploy(context, outputChannel, telemetryContext);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.DeviceCompile,
|
||||
EventNames.deviceCompileEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return deviceOperator.compile(context, outputChannel, telemetryContext);
|
||||
});
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.DeviceCompile,
|
||||
EventNames.deviceCompileEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return deviceOperator.compile(context, outputChannel, telemetryContext);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.DeviceUpload,
|
||||
EventNames.deviceUploadEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return deviceOperator.upload(context, outputChannel, telemetryContext);
|
||||
});
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.DeviceUpload,
|
||||
EventNames.deviceUploadEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return deviceOperator.upload(context, outputChannel, telemetryContext);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.ConfigureDevice, EventNames.configDeviceSettingsEvent,
|
||||
true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return deviceOperator.configDeviceSettings(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.ConfigureDevice, EventNames.configDeviceSettingsEvent,
|
||||
true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return deviceOperator.configDeviceSettings(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.Examples,
|
||||
EventNames.openExamplePageEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return exampleExplorer.selectBoard(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.Examples,
|
||||
EventNames.openExamplePageEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, _outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
return exampleExplorer.selectBoard(
|
||||
context, telemetryContext);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.ExampleInitialize, EventNames.loadExampleEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, name?: string, url?: string,
|
||||
boardId?: string): Promise<void> => {
|
||||
return exampleExplorer.initializeExample(
|
||||
context, outputChannel, telemetryContext, name, url, boardId);
|
||||
});
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.ExampleInitialize, EventNames.loadExampleEvent, true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, name?: string, url?: string,
|
||||
boardId?: string): Promise<void> => {
|
||||
return exampleExplorer.initializeExample(
|
||||
context, outputChannel, telemetryContext, name, url, boardId);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.SendTelemetry,
|
||||
EventNames.openTutorial, true, async () => {});
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.SendTelemetry,
|
||||
EventNames.openTutorial, true, async () => {
|
||||
// Do nothing.
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.IotPnPGenerateCode, EventNames.scaffoldDeviceStubEvent,
|
||||
true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
const codeGenerator = new CodeGeneratorCore();
|
||||
return codeGenerator.generateDeviceCodeStub(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
context, telemetryWorker, outputChannel,
|
||||
WorkbenchCommands.IotPnPGenerateCode, EventNames.scaffoldDeviceStubEvent,
|
||||
true,
|
||||
async(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> => {
|
||||
const codeGenerator = new CodeGeneratorCore();
|
||||
return codeGenerator.generateDeviceCodeStub(
|
||||
context, outputChannel, telemetryContext);
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.Help,
|
||||
EventNames.help, true, async () => {
|
||||
const boardId = ConfigHandler.get<string>(ConfigKey.boardId);
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.Help,
|
||||
EventNames.help, true, async () => {
|
||||
const boardId = ConfigHandler.get<string>(ConfigKey.boardId);
|
||||
|
||||
if (boardId) {
|
||||
const boardListFolderPath = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
const boardProvider = new BoardProvider(boardListFolderPath);
|
||||
const board = boardProvider.find({id: boardId});
|
||||
if (boardId) {
|
||||
const boardListFolderPath = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
const boardProvider = new BoardProvider(boardListFolderPath);
|
||||
const board = boardProvider.find({ id: boardId });
|
||||
|
||||
if (board && board.helpUrl) {
|
||||
await vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen, vscode.Uri.parse(board.helpUrl));
|
||||
return;
|
||||
}
|
||||
}
|
||||
const workbenchHelpUrl =
|
||||
'https://github.com/microsoft/vscode-iot-workbench/blob/master/README.md';
|
||||
await vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen, vscode.Uri.parse(workbenchHelpUrl));
|
||||
return;
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.Workbench,
|
||||
EventNames.setProjectDefaultPath, true, async () => {
|
||||
const isLocal = RemoteExtension.checkLocalBeforeRunCommand(context);
|
||||
if (!isLocal) {
|
||||
if (board && board.helpUrl) {
|
||||
await vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen, vscode.Uri.parse(board.helpUrl));
|
||||
return;
|
||||
}
|
||||
const settings = await IoTWorkbenchSettings.getInstance();
|
||||
await settings.setWorkbenchPath();
|
||||
}
|
||||
const workbenchHelpUrl =
|
||||
'https://github.com/microsoft/vscode-iot-workbench/blob/master/README.md';
|
||||
await vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen, vscode.Uri.parse(workbenchHelpUrl));
|
||||
return;
|
||||
});
|
||||
|
||||
initCommandWithTelemetry(
|
||||
context, telemetryWorker, outputChannel, WorkbenchCommands.Workbench,
|
||||
EventNames.setProjectDefaultPath, true, async () => {
|
||||
const isLocal = RemoteExtension.checkLocalBeforeRunCommand(context);
|
||||
if (!isLocal) {
|
||||
return;
|
||||
});
|
||||
}
|
||||
const settings = await IoTWorkbenchSettings.getInstance();
|
||||
await settings.setWorkbenchPath();
|
||||
return;
|
||||
});
|
||||
|
||||
initCommand(context, WorkbenchCommands.OpenUri, async (uri: string) => {
|
||||
vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen, vscode.Uri.parse(uri));
|
||||
VscodeCommands.VscodeOpen, vscode.Uri.parse(uri));
|
||||
});
|
||||
|
||||
initCommand(context, WorkbenchCommands.HttpRequest, async (uri: string) => {
|
||||
|
@ -228,307 +533,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export async function deactivate() {}
|
||||
|
||||
function enableUsbDetector(
|
||||
context: vscode.ExtensionContext,
|
||||
outputChannel: vscode.OutputChannel): void {
|
||||
if (RemoteExtension.isRemote(context)) {
|
||||
return;
|
||||
}
|
||||
// delay to detect usb
|
||||
const usbDetectorModule =
|
||||
impor('./usbDetector') as typeof import('./usbDetector');
|
||||
|
||||
const usbDetector = new usbDetectorModule.UsbDetector(context, outputChannel);
|
||||
usbDetector.startListening(context);
|
||||
}
|
||||
|
||||
function printHello(context: vscode.ExtensionContext) {
|
||||
const extension = WorkbenchExtension.getExtension(context);
|
||||
if (!extension) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extensionId = extension.id;
|
||||
console.log(`Congratulations, your extension ${extensionId} is now active!`);
|
||||
}
|
||||
|
||||
|
||||
function initCommandWithTelemetry(
|
||||
context: vscode.ExtensionContext, telemetryWorker: TelemetryWorker,
|
||||
outputChannel: vscode.OutputChannel, command: WorkbenchCommands,
|
||||
eventName: string, enableSurvey: boolean,
|
||||
// tslint:disable-next-line:no-any
|
||||
callback: (
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
// tslint:disable-next-line:no-any
|
||||
telemetrycontext: TelemetryContext, ...args: any[]) => any,
|
||||
// tslint:disable-next-line:no-any
|
||||
additionalProperties?: {[key: string]: string}): void {
|
||||
context.subscriptions.push(vscode.commands.registerCommand(
|
||||
command,
|
||||
async (...commandArgs) => telemetryWorker.callCommandWithTelemetry(
|
||||
context, outputChannel, eventName, enableSurvey, callback,
|
||||
additionalProperties, ...commandArgs)));
|
||||
}
|
||||
|
||||
function initCommand(
|
||||
context: vscode.ExtensionContext, command: WorkbenchCommands,
|
||||
// tslint:disable-next-line:no-any
|
||||
callback: (...args: any[]) => Promise<any>): void {
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand(command, callback));
|
||||
}
|
||||
|
||||
// DigitalTwin extension part
|
||||
function initDigitalTwin(
|
||||
context: vscode.ExtensionContext,
|
||||
outputChannel: vscode.OutputChannel): void {
|
||||
const colorizedChannel = new ColorizedChannel(Constants.CHANNEL_NAME);
|
||||
context.subscriptions.push(colorizedChannel);
|
||||
const deviceModelManager = new DeviceModelManager(context, colorizedChannel);
|
||||
const modelRepositoryManager = new ModelRepositoryManager(
|
||||
context, Constants.WEB_VIEW_PATH, colorizedChannel);
|
||||
|
||||
DigitalTwinUtility.init(modelRepositoryManager, outputChannel);
|
||||
initIntelliSense(context);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.CreateInterface,
|
||||
async():
|
||||
Promise<void> => {
|
||||
return deviceModelManager.createModel(ModelType.Interface);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.CreateCapabilityModel,
|
||||
async():
|
||||
Promise<void> => {
|
||||
return deviceModelManager.createModel(ModelType.CapabilityModel);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.OpenRepository,
|
||||
async():
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.signIn();
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.SignOutRepository,
|
||||
async():
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.signOut();
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
true,
|
||||
Command.SubmitFiles,
|
||||
async(telemetryContext: TelemetryContext):
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.submitFiles(telemetryContext);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
false,
|
||||
Command.DeleteModels,
|
||||
async(
|
||||
telemetryContext: TelemetryContext, publicRepository: boolean,
|
||||
modelIds: string[]):
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.deleteModels(
|
||||
publicRepository, modelIds);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
false,
|
||||
Command.DownloadModels,
|
||||
async(
|
||||
telemetryContext: TelemetryContext, publicRepository: boolean,
|
||||
modelIds: string[]):
|
||||
Promise<void> => {
|
||||
return modelRepositoryManager.downloadModels(
|
||||
publicRepository, modelIds);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
false,
|
||||
Command.SearchInterface,
|
||||
async(
|
||||
telemetryContext: TelemetryContext,
|
||||
publicRepository: boolean,
|
||||
keyword?: string,
|
||||
pageSize?: number,
|
||||
continuationToken?: string,
|
||||
):
|
||||
Promise<SearchResult> => {
|
||||
return modelRepositoryManager.searchModel(
|
||||
ModelType.Interface,
|
||||
publicRepository,
|
||||
keyword,
|
||||
pageSize,
|
||||
continuationToken,
|
||||
);
|
||||
},
|
||||
);
|
||||
initDigitalTwinCommand(
|
||||
context,
|
||||
telemetryWorker,
|
||||
colorizedChannel,
|
||||
false,
|
||||
Command.SearchCapabilityModel,
|
||||
async(
|
||||
telemetryContext: TelemetryContext,
|
||||
publicRepository: boolean,
|
||||
keyword?: string,
|
||||
pageSize?: number,
|
||||
continuationToken?: string,
|
||||
):
|
||||
Promise<SearchResult> => {
|
||||
return modelRepositoryManager.searchModel(
|
||||
ModelType.CapabilityModel,
|
||||
publicRepository,
|
||||
keyword,
|
||||
pageSize,
|
||||
continuationToken,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function initIntelliSense(context: vscode.ExtensionContext): void {
|
||||
// init DigitalTwin graph
|
||||
IntelliSenseUtility.initGraph(context);
|
||||
// register providers of completionItem and hover
|
||||
const selector: vscode.DocumentSelector = {
|
||||
language: 'json',
|
||||
scheme: 'file',
|
||||
};
|
||||
context.subscriptions.push(
|
||||
vscode.languages.registerCompletionItemProvider(
|
||||
selector,
|
||||
new DigitalTwinCompletionItemProvider(),
|
||||
Constants.COMPLETION_TRIGGER,
|
||||
),
|
||||
);
|
||||
context.subscriptions.push(vscode.languages.registerHoverProvider(
|
||||
selector, new DigitalTwinHoverProvider()));
|
||||
// register diagnostic
|
||||
let pendingDiagnostic: NodeJS.Timer;
|
||||
const diagnosticCollection: vscode.DiagnosticCollection =
|
||||
vscode.languages.createDiagnosticCollection(
|
||||
Constants.CHANNEL_NAME,
|
||||
);
|
||||
const diagnosticProvider = new DigitalTwinDiagnosticProvider();
|
||||
const activeTextEditor: vscode.TextEditor|undefined =
|
||||
vscode.window.activeTextEditor;
|
||||
if (activeTextEditor) {
|
||||
diagnosticProvider.updateDiagnostics(
|
||||
activeTextEditor.document, diagnosticCollection);
|
||||
}
|
||||
context.subscriptions.push(diagnosticCollection);
|
||||
context.subscriptions.push(
|
||||
vscode.window.onDidChangeActiveTextEditor((event) => {
|
||||
if (event) {
|
||||
diagnosticProvider.updateDiagnostics(
|
||||
event.document, diagnosticCollection);
|
||||
}
|
||||
}),
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidChangeTextDocument((event) => {
|
||||
if (event) {
|
||||
if (pendingDiagnostic) {
|
||||
clearTimeout(pendingDiagnostic);
|
||||
}
|
||||
pendingDiagnostic = setTimeout(
|
||||
() => diagnosticProvider.updateDiagnostics(
|
||||
event.document, diagnosticCollection),
|
||||
Constants.DEFAULT_TIMER_MS,
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidCloseTextDocument(
|
||||
(document) => diagnosticCollection.delete(document.uri)),
|
||||
);
|
||||
}
|
||||
|
||||
function initDigitalTwinCommand(
|
||||
context: vscode.ExtensionContext,
|
||||
telemetryWorker: TelemetryWorker,
|
||||
outputChannel: ColorizedChannel,
|
||||
enableSurvey: boolean,
|
||||
command: Command,
|
||||
// tslint:disable-next-line:no-any
|
||||
callback: (telemetryContext: TelemetryContext, ...args: any[]) =>
|
||||
// tslint:disable-next-line:no-any
|
||||
Promise<any>,
|
||||
): void {
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
command,
|
||||
// tslint:disable-next-line:no-any
|
||||
async (...args: any[]) => {
|
||||
const start: number = Date.now();
|
||||
const telemetryContext: TelemetryContext =
|
||||
telemetryWorker.createContext();
|
||||
try {
|
||||
return await callback(telemetryContext, ...args);
|
||||
} catch (error) {
|
||||
telemetryContext.properties.error = error.name;
|
||||
telemetryContext.properties.errorMessage = error.message;
|
||||
if (error instanceof UserCancelledError) {
|
||||
telemetryContext.properties.result = TelemetryResult.Cancelled;
|
||||
outputChannel.warn(error.message);
|
||||
} else {
|
||||
telemetryContext.properties.result = TelemetryResult.Failed;
|
||||
UI.showNotification(MessageType.Error, error.message);
|
||||
if (error instanceof ProcessError) {
|
||||
const message = `${error.message}\n${error.stack}`;
|
||||
outputChannel.error(message, error.component);
|
||||
} else {
|
||||
outputChannel.error(error.message);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
telemetryContext.measurements.duration =
|
||||
(Date.now() - start) / 1000;
|
||||
telemetryWorker.sendEvent(command, telemetryContext);
|
||||
outputChannel.show();
|
||||
if (enableSurvey) {
|
||||
NSAT.takeSurvey(context);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
export async function deactivate(): Promise<void> {
|
||||
// Do nothing.
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// reference code from https://github.com/0815fox/DefinitelyTyped
|
||||
|
||||
declare module "getmac" {
|
||||
function getMac(opts:(err:Error,macAddress:string)=>void):void;
|
||||
function isMac(macAddress:string):boolean;
|
||||
function getMac(opts: (err: Error,macAddress: string) => void): void;
|
||||
function isMac(macAddress: string): boolean;
|
||||
}
|
30
src/nsat.ts
30
src/nsat.ts
|
@ -3,11 +3,11 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import {commands, ExtensionContext, Uri, window} from 'vscode';
|
||||
import {EventNames} from './constants';
|
||||
import {TelemetryWorker} from './telemetry';
|
||||
import {WorkbenchExtension} from './WorkbenchExtension';
|
||||
import {VscodeCommands} from './common/Commands';
|
||||
import { commands, ExtensionContext, Uri, window } from 'vscode';
|
||||
import { EventNames } from './constants';
|
||||
import { TelemetryWorker } from './telemetry';
|
||||
import { WorkbenchExtension } from './WorkbenchExtension';
|
||||
import { VscodeCommands } from './common/Commands';
|
||||
|
||||
const NSAT_SURVEY_URL = 'https://aka.ms/vscode-iot-workbench-survey';
|
||||
const PROBABILITY = 1;
|
||||
|
@ -20,7 +20,7 @@ const SKIP_VERSION_KEY = 'nsat/skipVersion';
|
|||
const IS_CANDIDATE_KEY = 'nsat/isCandidate';
|
||||
|
||||
export class NSAT {
|
||||
static async takeSurvey(context: ExtensionContext) {
|
||||
static async takeSurvey(context: ExtensionContext): Promise<void> {
|
||||
const globalState = context.globalState;
|
||||
const skipVersion = globalState.get(SKIP_VERSION_KEY, '');
|
||||
if (skipVersion) {
|
||||
|
@ -63,14 +63,14 @@ export class NSAT {
|
|||
|
||||
const take = {
|
||||
title: 'Take Survey',
|
||||
run: async () => {
|
||||
run: async (): Promise<void> => {
|
||||
telemetryContext.properties.message = 'nsat.survey/takeShortSurvey';
|
||||
telemetryWorker.sendEvent(EventNames.nsatsurvery, telemetryContext);
|
||||
commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen,
|
||||
Uri.parse(`${NSAT_SURVEY_URL}?o=${
|
||||
encodeURIComponent(process.platform)}&v=${
|
||||
encodeURIComponent(extensionVersion)}`));
|
||||
VscodeCommands.VscodeOpen,
|
||||
Uri.parse(`${NSAT_SURVEY_URL}?o=${
|
||||
encodeURIComponent(process.platform)}&v=${
|
||||
encodeURIComponent(extensionVersion)}`));
|
||||
await globalState.update(IS_CANDIDATE_KEY, false);
|
||||
await globalState.update(SKIP_VERSION_KEY, extensionVersion);
|
||||
await globalState.update(TAKE_SURVEY_DATE_KEY, date);
|
||||
|
@ -78,7 +78,7 @@ export class NSAT {
|
|||
};
|
||||
const remind = {
|
||||
title: 'Remind Me Later',
|
||||
run: async () => {
|
||||
run: async (): Promise<void> => {
|
||||
telemetryContext.properties.message = 'nsat.survey/remindMeLater';
|
||||
telemetryWorker.sendEvent(EventNames.nsatsurvery, telemetryContext);
|
||||
await globalState.update(SESSION_COUNT_KEY, 0);
|
||||
|
@ -86,7 +86,7 @@ export class NSAT {
|
|||
};
|
||||
const never = {
|
||||
title: 'Don\'t Show Again',
|
||||
run: async () => {
|
||||
run: async (): Promise<void> => {
|
||||
telemetryContext.properties.message = 'nsat.survey/dontShowAgain';
|
||||
telemetryWorker.sendEvent(EventNames.nsatsurvery, telemetryContext);
|
||||
await globalState.update(IS_CANDIDATE_KEY, false);
|
||||
|
@ -97,8 +97,8 @@ export class NSAT {
|
|||
telemetryContext.properties.message = 'nsat.survey/userAsked';
|
||||
telemetryWorker.sendEvent(EventNames.nsatsurvery, telemetryContext);
|
||||
const button = await window.showInformationMessage(
|
||||
'Do you mind taking a quick feedback survey about the Azure IoT Device Workbench Extension for VS Code?',
|
||||
take, remind, never);
|
||||
'Do you mind taking a quick feedback survey about the Azure IoT Device Workbench Extension for VS Code?',
|
||||
take, remind, never);
|
||||
await (button || remind).run();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ import * as vscode from 'vscode';
|
|||
import * as path from 'path';
|
||||
import * as utils from './utils';
|
||||
|
||||
import {TelemetryContext} from './telemetry';
|
||||
import {FileNames, ScaffoldType, PlatformType, TemplateTag} from './constants';
|
||||
import {IoTWorkbenchSettings} from './IoTSettings';
|
||||
import {FileUtility} from './FileUtility';
|
||||
import {ProjectTemplate, ProjectTemplateType, TemplatesType} from './Models/Interfaces/ProjectTemplate';
|
||||
import {RemoteExtension} from './Models/RemoteExtension';
|
||||
import {CancelOperationError} from './CancelOperationError';
|
||||
import { TelemetryContext } from './telemetry';
|
||||
import { FileNames, ScaffoldType, PlatformType, TemplateTag } from './constants';
|
||||
import { IoTWorkbenchSettings } from './IoTSettings';
|
||||
import { FileUtility } from './FileUtility';
|
||||
import { ProjectTemplate, ProjectTemplateType, TemplatesType } from './Models/Interfaces/ProjectTemplate';
|
||||
import { RemoteExtension } from './Models/RemoteExtension';
|
||||
import { CancelOperationError } from './CancelOperationError';
|
||||
|
||||
const impor = require('impor')(__dirname);
|
||||
const ioTWorkspaceProjectModule = impor('./Models/IoTWorkspaceProject') as
|
||||
|
@ -30,8 +30,8 @@ const constants = {
|
|||
|
||||
export class ProjectInitializer {
|
||||
async InitializeProject(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext) {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext): Promise<void> {
|
||||
// Only create project when not in remote environment
|
||||
const isLocal = RemoteExtension.checkLocalBeforeRunCommand(context);
|
||||
if (!isLocal) {
|
||||
|
@ -48,103 +48,102 @@ export class ProjectInitializer {
|
|||
|
||||
// Initial project
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
title: 'Project initialization',
|
||||
location: vscode.ProgressLocation.Window,
|
||||
},
|
||||
async (progress) => {
|
||||
progress.report({
|
||||
message: 'Updating a list of available template',
|
||||
});
|
||||
{
|
||||
title: 'Project initialization',
|
||||
location: vscode.ProgressLocation.Window,
|
||||
},
|
||||
async (progress) => {
|
||||
progress.report({
|
||||
message: 'Updating a list of available template',
|
||||
});
|
||||
|
||||
const scaffoldType = ScaffoldType.Local;
|
||||
const scaffoldType = ScaffoldType.Local;
|
||||
|
||||
// Step 1: Get project name
|
||||
const projectPath =
|
||||
// Step 1: Get project name
|
||||
const projectPath =
|
||||
await this.generateProjectFolder(telemetryContext, scaffoldType);
|
||||
if (!projectPath) {
|
||||
throw new CancelOperationError(
|
||||
`Project initialization cancelled: Project name input cancelled.`);
|
||||
}
|
||||
if (!projectPath) {
|
||||
throw new CancelOperationError(
|
||||
`Project initialization cancelled: Project name input cancelled.`);
|
||||
}
|
||||
|
||||
// Step 2: Select platform
|
||||
const platformSelection =
|
||||
// Step 2: Select platform
|
||||
const platformSelection =
|
||||
await utils.selectPlatform(scaffoldType, context);
|
||||
if (!platformSelection) {
|
||||
throw new CancelOperationError(
|
||||
`Project initialization cancelled: Platform selection cancelled.`);
|
||||
} else {
|
||||
telemetryContext.properties.platform = platformSelection.label;
|
||||
}
|
||||
if (!platformSelection) {
|
||||
throw new CancelOperationError(
|
||||
`Project initialization cancelled: Platform selection cancelled.`);
|
||||
} else {
|
||||
telemetryContext.properties.platform = platformSelection.label;
|
||||
}
|
||||
|
||||
// Step 3: Select template
|
||||
let template: ProjectTemplate|undefined;
|
||||
const resourceRootPath = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
const templateJsonFilePath =
|
||||
// Step 3: Select template
|
||||
const resourceRootPath = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
const templateJsonFilePath =
|
||||
path.join(resourceRootPath, FileNames.templateFileName);
|
||||
const templateJsonFileString =
|
||||
const templateJsonFileString =
|
||||
await FileUtility.readFile(
|
||||
scaffoldType, templateJsonFilePath, 'utf8') as string;
|
||||
const templateJson = JSON.parse(templateJsonFileString);
|
||||
if (!templateJson) {
|
||||
throw new Error(`Fail to load template json.`);
|
||||
}
|
||||
scaffoldType, templateJsonFilePath, 'utf8') as string;
|
||||
const templateJson = JSON.parse(templateJsonFileString);
|
||||
if (!templateJson) {
|
||||
throw new Error(`Fail to load template json.`);
|
||||
}
|
||||
|
||||
let templateName: string|undefined;
|
||||
if (platformSelection.label === PlatformType.Arduino) {
|
||||
const templateSelection =
|
||||
let templateName: string|undefined;
|
||||
if (platformSelection.label === PlatformType.Arduino) {
|
||||
const templateSelection =
|
||||
await this.selectTemplate(templateJson, PlatformType.Arduino);
|
||||
|
||||
if (!templateSelection) {
|
||||
throw new CancelOperationError(
|
||||
`Project initialization cancelled: Project template selection cancelled.`);
|
||||
} else {
|
||||
telemetryContext.properties.template = templateSelection.label;
|
||||
if (templateSelection.label === constants.noDeviceMessage) {
|
||||
await utils.takeNoDeviceSurvey(telemetryContext, context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
templateName = templateSelection.label;
|
||||
if (!templateSelection) {
|
||||
throw new CancelOperationError(
|
||||
`Project initialization cancelled: Project template selection cancelled.`);
|
||||
} else {
|
||||
// If choose Embedded Linux platform, generate C project template
|
||||
// directly
|
||||
templateName = constants.embeddedLinuxProjectName;
|
||||
telemetryContext.properties.template = templateSelection.label;
|
||||
if (templateSelection.label === constants.noDeviceMessage) {
|
||||
await utils.takeNoDeviceSurvey(telemetryContext, context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
templateName = templateSelection.label;
|
||||
} else {
|
||||
// If choose Embedded Linux platform, generate C project template
|
||||
// directly
|
||||
templateName = constants.embeddedLinuxProjectName;
|
||||
}
|
||||
|
||||
template =
|
||||
const template =
|
||||
templateJson.templates.find((template: ProjectTemplate) => {
|
||||
return template.platform === platformSelection.label &&
|
||||
template.name === templateName;
|
||||
});
|
||||
if (!template) {
|
||||
throw new Error(
|
||||
`Fail to find the wanted project template in template json file.`);
|
||||
}
|
||||
if (!template) {
|
||||
throw new Error(
|
||||
`Fail to find the wanted project template in template json file.`);
|
||||
}
|
||||
|
||||
// Step 4: Load the list of template files
|
||||
const projectTemplateType: ProjectTemplateType =
|
||||
// Step 4: Load the list of template files
|
||||
const projectTemplateType: ProjectTemplateType =
|
||||
utils.getEnumKeyByEnumValue(ProjectTemplateType, template.type);
|
||||
|
||||
const templateFolder = path.join(resourceRootPath, template.path);
|
||||
const templateFilesInfo =
|
||||
const templateFolder = path.join(resourceRootPath, template.path);
|
||||
const templateFilesInfo =
|
||||
await utils.getTemplateFilesInfo(templateFolder);
|
||||
|
||||
let project;
|
||||
if (template.platform === PlatformType.EmbeddedLinux) {
|
||||
project = new ioTContainerizedProjectModule.IoTContainerizedProject(
|
||||
context, channel, telemetryContext, projectPath);
|
||||
} else if (template.platform === PlatformType.Arduino) {
|
||||
project = new ioTWorkspaceProjectModule.IoTWorkspaceProject(
|
||||
context, channel, telemetryContext, projectPath);
|
||||
} else {
|
||||
throw new Error('unsupported platform');
|
||||
}
|
||||
await project.create(
|
||||
templateFilesInfo, projectTemplateType, template.boardId,
|
||||
openInNewWindow);
|
||||
});
|
||||
let project;
|
||||
if (template.platform === PlatformType.EmbeddedLinux) {
|
||||
project = new ioTContainerizedProjectModule.IoTContainerizedProject(
|
||||
context, channel, telemetryContext, projectPath);
|
||||
} else if (template.platform === PlatformType.Arduino) {
|
||||
project = new ioTWorkspaceProjectModule.IoTWorkspaceProject(
|
||||
context, channel, telemetryContext, projectPath);
|
||||
} else {
|
||||
throw new Error('unsupported platform');
|
||||
}
|
||||
await project.create(
|
||||
templateFilesInfo, projectTemplateType, template.boardId,
|
||||
openInNewWindow);
|
||||
});
|
||||
}
|
||||
|
||||
private async selectTemplate(templateJson: TemplatesType, platform: string):
|
||||
|
@ -152,7 +151,7 @@ export class ProjectInitializer {
|
|||
const result =
|
||||
templateJson.templates.filter((template: ProjectTemplate) => {
|
||||
return (
|
||||
template.platform === platform &&
|
||||
template.platform === platform &&
|
||||
template.tag === TemplateTag.General);
|
||||
});
|
||||
|
||||
|
@ -178,8 +177,8 @@ export class ProjectInitializer {
|
|||
}
|
||||
|
||||
private async generateProjectFolder(
|
||||
telemetryContext: TelemetryContext,
|
||||
scaffoldType: ScaffoldType): Promise<string|undefined> {
|
||||
telemetryContext: TelemetryContext,
|
||||
scaffoldType: ScaffoldType): Promise<string|undefined> {
|
||||
// Get default workbench path.
|
||||
const settings = await IoTWorkbenchSettings.getInstance();
|
||||
const workbench = settings.getWorkbenchPath();
|
||||
|
@ -192,6 +191,7 @@ export class ProjectInitializer {
|
|||
let counter = 0;
|
||||
const name = constants.defaultProjectName;
|
||||
let candidateName = name;
|
||||
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
|
||||
while (true) {
|
||||
const projectPath = path.join(projectRootPath, candidateName);
|
||||
const isValid = await this.isProjectPathValid(scaffoldType, projectPath);
|
||||
|
@ -209,7 +209,7 @@ export class ProjectInitializer {
|
|||
ignoreFocusOut: true,
|
||||
validateInput: async (projectName: string) => {
|
||||
if (!/^([a-z0-9_]|[a-z0-9_][-a-z0-9_.]*[a-z0-9_])(\.ino)?$/i.test(
|
||||
projectName)) {
|
||||
projectName)) {
|
||||
return 'Project name can only contain letters, numbers, "-" and ".", and cannot start or end with "-" or ".".';
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,7 @@ export class ProjectInitializer {
|
|||
}
|
||||
|
||||
private async isProjectPathValid(
|
||||
scaffoldType: ScaffoldType, projectPath: string): Promise<boolean> {
|
||||
scaffoldType: ScaffoldType, projectPath: string): Promise<boolean> {
|
||||
const projectPathExists =
|
||||
await FileUtility.fileExists(scaffoldType, projectPath);
|
||||
const projectDirectoryExists =
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
import * as vscode from 'vscode';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
|
||||
import {CancelOperationError} from './CancelOperationError';
|
||||
import {DevelopEnvironment} from './constants';
|
||||
import {ExceptionHelper} from './exceptionHelper';
|
||||
import {RemoteExtension} from './Models/RemoteExtension';
|
||||
import {NSAT} from './nsat';
|
||||
import {WorkbenchExtension} from './WorkbenchExtension';
|
||||
import { CancelOperationError } from './CancelOperationError';
|
||||
import { DevelopEnvironment } from './constants';
|
||||
import { ExceptionHelper } from './exceptionHelper';
|
||||
import { RemoteExtension } from './Models/RemoteExtension';
|
||||
import { NSAT } from './nsat';
|
||||
import { WorkbenchExtension } from './WorkbenchExtension';
|
||||
|
||||
|
||||
interface PackageInfo {
|
||||
|
@ -50,11 +50,11 @@ export class TelemetryWorker {
|
|||
}
|
||||
if (!packageInfo.aiKey) {
|
||||
console.log(
|
||||
'Unable to initialize telemetry, please make sure AIKey is set in package.json');
|
||||
'Unable to initialize telemetry, please make sure AIKey is set in package.json');
|
||||
return;
|
||||
}
|
||||
this._reporter = new TelemetryReporter(
|
||||
packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||
packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||
this._isInternal = TelemetryWorker.isInternalUser();
|
||||
}
|
||||
|
||||
|
@ -70,8 +70,8 @@ export class TelemetryWorker {
|
|||
*/
|
||||
private static isInternalUser(): boolean {
|
||||
const userDomain: string = process.env.USERDNSDOMAIN ?
|
||||
process.env.USERDNSDOMAIN.toLowerCase() :
|
||||
'';
|
||||
process.env.USERDNSDOMAIN.toLowerCase() :
|
||||
'';
|
||||
return userDomain.endsWith('microsoft.com');
|
||||
}
|
||||
|
||||
|
@ -79,14 +79,14 @@ export class TelemetryWorker {
|
|||
* Create telemetry context
|
||||
*/
|
||||
createContext(): TelemetryContext {
|
||||
const context: TelemetryContext = {properties: {}, measurements: {}};
|
||||
const context: TelemetryContext = { properties: {}, measurements: {} };
|
||||
context.properties.result = TelemetryResult.Succeeded;
|
||||
context.properties.isInternal = this._isInternal.toString();
|
||||
if (this._extensionContext) {
|
||||
context.properties.developEnvironment =
|
||||
RemoteExtension.isRemote(this._extensionContext) ?
|
||||
DevelopEnvironment.RemoteEnv :
|
||||
DevelopEnvironment.LocalEnv;
|
||||
DevelopEnvironment.RemoteEnv :
|
||||
DevelopEnvironment.LocalEnv;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ export class TelemetryWorker {
|
|||
telemetryContext = this.createContext();
|
||||
}
|
||||
this._reporter.sendTelemetryEvent(
|
||||
eventName, telemetryContext.properties, telemetryContext.measurements);
|
||||
eventName, telemetryContext.properties, telemetryContext.measurements);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,25 +118,25 @@ export class TelemetryWorker {
|
|||
* @param additionalProperties
|
||||
*/
|
||||
async callCommandWithTelemetry(
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
eventName: string, enableSurvey: boolean,
|
||||
// tslint:disable-next-line:no-any
|
||||
callback:
|
||||
context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel,
|
||||
eventName: string, enableSurvey: boolean,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
callback:
|
||||
(context: vscode.ExtensionContext,
|
||||
outputChannel: vscode.OutputChannel,
|
||||
// tslint:disable-next-line:no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
telemetrycontext: TelemetryContext, ...args: any[]) => any,
|
||||
// tslint:disable-next-line:no-any
|
||||
additionalProperties?: {[key: string]: string},
|
||||
// tslint:disable-next-line:no-any
|
||||
...commandArgs: any[]): Promise<any> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
additionalProperties?: {[key: string]: string},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
...commandArgs: any[]): Promise<any> {
|
||||
const telemetryWorker = TelemetryWorker.getInstance(context);
|
||||
const telemetryContext = telemetryWorker.createContext();
|
||||
|
||||
const start: number = Date.now();
|
||||
if (additionalProperties) {
|
||||
for (const key of Object.keys(additionalProperties)) {
|
||||
if (!telemetryContext.properties.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(telemetryContext.properties, key)) {
|
||||
telemetryContext.properties[key] = additionalProperties[key];
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ export class TelemetryWorker {
|
|||
|
||||
try {
|
||||
return await callback(
|
||||
context, outputChannel, telemetryContext, ...commandArgs);
|
||||
context, outputChannel, telemetryContext, ...commandArgs);
|
||||
} catch (error) {
|
||||
telemetryContext.properties.errorMessage = error.message;
|
||||
let isPopupErrorMsg = true;
|
||||
|
@ -172,7 +172,7 @@ export class TelemetryWorker {
|
|||
/**
|
||||
* dispose telemetry worker
|
||||
*/
|
||||
async dispose() {
|
||||
async dispose(): Promise<void> {
|
||||
if (this._reporter) {
|
||||
await this._reporter.dispose();
|
||||
}
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import {VSCExpress} from 'vscode-express';
|
||||
import { VSCExpress } from 'vscode-express';
|
||||
|
||||
import {ArduinoPackageManager} from './ArduinoPackageManager';
|
||||
import {BoardProvider} from './boardProvider';
|
||||
import {ConfigKey, EventNames, FileNames, OSPlatform} from './constants';
|
||||
import {TelemetryWorker} from './telemetry';
|
||||
import {shouldShowLandingPage} from './utils';
|
||||
import { ArduinoPackageManager } from './ArduinoPackageManager';
|
||||
import { BoardProvider } from './boardProvider';
|
||||
import { ConfigKey, EventNames, FileNames, OSPlatform } from './constants';
|
||||
import { TelemetryWorker } from './telemetry';
|
||||
import { shouldShowLandingPage } from './utils';
|
||||
import { Board } from './Models/Interfaces/Board';
|
||||
|
||||
export interface DeviceInfo {
|
||||
vendorId: number;
|
||||
|
@ -19,7 +20,7 @@ export interface DeviceInfo {
|
|||
|
||||
export class UsbDetector {
|
||||
private static _vscexpress: VSCExpress|undefined;
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private static _usbDetector: any;
|
||||
|
||||
constructor(
|
||||
|
@ -34,53 +35,53 @@ export class UsbDetector {
|
|||
}
|
||||
}
|
||||
|
||||
getBoardFromDeviceInfo(device: DeviceInfo) {
|
||||
getBoardFromDeviceInfo(device: DeviceInfo): Board|undefined {
|
||||
if (device.vendorId && device.productId) {
|
||||
const boardFolderPath = this.context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName));
|
||||
const boardProvider = new BoardProvider(boardFolderPath);
|
||||
const board = boardProvider.find(
|
||||
{vendorId: device.vendorId, productId: device.productId});
|
||||
{ vendorId: device.vendorId, productId: device.productId });
|
||||
|
||||
return board;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
showLandingPage(device: DeviceInfo) {
|
||||
showLandingPage(device: DeviceInfo): void {
|
||||
const board = this.getBoardFromDeviceInfo(device);
|
||||
|
||||
if (board) {
|
||||
const telemetryWorker = TelemetryWorker.getInstance(this.context);
|
||||
|
||||
telemetryWorker.callCommandWithTelemetry(
|
||||
this.context, this.channel, EventNames.detectBoard,
|
||||
false, async () => {
|
||||
if (board.exampleUrl) {
|
||||
ArduinoPackageManager.installBoard(board);
|
||||
this.context, this.channel, EventNames.detectBoard,
|
||||
false, async () => {
|
||||
if (board.exampleUrl) {
|
||||
ArduinoPackageManager.installBoard(board);
|
||||
|
||||
const exampleUrl = 'example.html?board=' + board.id +
|
||||
const exampleUrl = 'example.html?board=' + board.id +
|
||||
'&url=' + encodeURIComponent(board.exampleUrl || '');
|
||||
UsbDetector._vscexpress = UsbDetector._vscexpress ||
|
||||
UsbDetector._vscexpress = UsbDetector._vscexpress ||
|
||||
new VSCExpress(this.context, 'views');
|
||||
UsbDetector._vscexpress.open(
|
||||
exampleUrl,
|
||||
board.examplePageName +
|
||||
UsbDetector._vscexpress.open(
|
||||
exampleUrl,
|
||||
board.examplePageName +
|
||||
' samples - Azure IoT Device Workbench',
|
||||
vscode.ViewColumn.One, {
|
||||
enableScripts: true,
|
||||
enableCommandUris: true,
|
||||
retainContextWhenHidden: true
|
||||
});
|
||||
}
|
||||
}, {}, {board: board.name});
|
||||
vscode.ViewColumn.One, {
|
||||
enableScripts: true,
|
||||
enableCommandUris: true,
|
||||
retainContextWhenHidden: true
|
||||
});
|
||||
}
|
||||
}, {}, { board: board.name });
|
||||
}
|
||||
|
||||
// Will not auto pop up landing page next time.
|
||||
this.context.globalState.update(ConfigKey.hasPopUp, true);
|
||||
}
|
||||
|
||||
async startListening(context: vscode.ExtensionContext) {
|
||||
async startListening(context: vscode.ExtensionContext): Promise<void> {
|
||||
const enableUSBDetection = shouldShowLandingPage(context);
|
||||
if (os.platform() === OSPlatform.LINUX || !enableUSBDetection) {
|
||||
return;
|
||||
|
@ -98,7 +99,7 @@ export class UsbDetector {
|
|||
|
||||
devices.forEach(device => {
|
||||
if (uniqueDevices.findIndex(
|
||||
item => item.vendorId === device.vendorId &&
|
||||
item => item.vendorId === device.vendorId &&
|
||||
item.productId === device.productId) < 0) {
|
||||
uniqueDevices.push(device);
|
||||
}
|
||||
|
|
581
src/utils.ts
581
src/utils.ts
|
@ -6,23 +6,23 @@ import * as crypto from 'crypto';
|
|||
import * as fs from 'fs-plus';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import {MessageItem} from 'vscode';
|
||||
import { MessageItem } from 'vscode';
|
||||
import * as sdk from 'vscode-iot-device-cube-sdk';
|
||||
import * as WinReg from 'winreg';
|
||||
|
||||
import {CancelOperationError} from './CancelOperationError';
|
||||
import {IoTCubeCommands, RemoteContainersCommands, VscodeCommands, WorkbenchCommands} from './common/Commands';
|
||||
import {AzureFunctionsLanguage, ConfigKey, FileNames, OperationType, PlatformType, ScaffoldType, TemplateTag} from './constants';
|
||||
import {DialogResponses} from './DialogResponses';
|
||||
import {FileUtility} from './FileUtility';
|
||||
import {ProjectHostType} from './Models/Interfaces/ProjectHostType';
|
||||
import {ProjectTemplate, TemplateFileInfo} from './Models/Interfaces/ProjectTemplate';
|
||||
import {Platform} from './Models/Interfaces/ProjectTemplate';
|
||||
import {IoTWorkbenchProjectBase} from './Models/IoTWorkbenchProjectBase';
|
||||
import {RemoteExtension} from './Models/RemoteExtension';
|
||||
import {ProjectEnvironmentConfiger} from './ProjectEnvironmentConfiger';
|
||||
import {TelemetryContext, TelemetryResult} from './telemetry';
|
||||
import {WorkbenchExtension} from './WorkbenchExtension';
|
||||
import { CancelOperationError } from './CancelOperationError';
|
||||
import { IoTCubeCommands, RemoteContainersCommands, VscodeCommands, WorkbenchCommands } from './common/Commands';
|
||||
import { AzureFunctionsLanguage, ConfigKey, FileNames, OperationType, PlatformType, ScaffoldType, TemplateTag } from './constants';
|
||||
import { DialogResponses } from './DialogResponses';
|
||||
import { FileUtility } from './FileUtility';
|
||||
import { ProjectHostType } from './Models/Interfaces/ProjectHostType';
|
||||
import { ProjectTemplate, TemplateFileInfo } from './Models/Interfaces/ProjectTemplate';
|
||||
import { Platform } from './Models/Interfaces/ProjectTemplate';
|
||||
import { IoTWorkbenchProjectBase } from './Models/IoTWorkbenchProjectBase';
|
||||
import { RemoteExtension } from './Models/RemoteExtension';
|
||||
import { ProjectEnvironmentConfiger } from './ProjectEnvironmentConfiger';
|
||||
import { TelemetryContext, TelemetryResult } from './telemetry';
|
||||
import { WorkbenchExtension } from './WorkbenchExtension';
|
||||
|
||||
const impor = require('impor')(__dirname);
|
||||
const ioTWorkspaceProjectModule = impor('./Models/IoTWorkspaceProject') as
|
||||
|
@ -33,38 +33,50 @@ const ioTContainerizedProjectModule =
|
|||
const raspberryPiDeviceModule = impor('./Models/RaspberryPiDevice') as
|
||||
typeof import('./Models/RaspberryPiDevice');
|
||||
|
||||
export function delay(ms: number) {
|
||||
export function delay(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function getRegistryValues(
|
||||
hive: string, key: string, name: string): Promise<string> {
|
||||
return new Promise(
|
||||
async (
|
||||
resolve: (value: string) => void, reject: (value: Error) => void) => {
|
||||
try {
|
||||
const regKey = new WinReg({hive, key});
|
||||
export function channelShowAndAppend(
|
||||
channel: vscode.OutputChannel, message: string): void {
|
||||
channel.show();
|
||||
channel.append(message);
|
||||
}
|
||||
|
||||
regKey.valueExists(name, (e, exists) => {
|
||||
if (e) {
|
||||
return reject(e);
|
||||
}
|
||||
if (exists) {
|
||||
regKey.get(name, (err, result) => {
|
||||
if (!err) {
|
||||
return resolve(result ? result.value : '');
|
||||
} else {
|
||||
return reject(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return resolve('');
|
||||
}
|
||||
});
|
||||
} catch (ex) {
|
||||
return reject(ex);
|
||||
}
|
||||
});
|
||||
export function channelShowAndAppendLine(
|
||||
channel: vscode.OutputChannel, message: string): void {
|
||||
channel.show();
|
||||
channel.appendLine(message);
|
||||
}
|
||||
|
||||
export function getRegistryValues(
|
||||
hive: string, key: string, name: string): Promise<string> {
|
||||
return new Promise(
|
||||
(
|
||||
resolve: (value: string) => void, reject: (value: Error) => void) => {
|
||||
try {
|
||||
const regKey = new WinReg({ hive, key });
|
||||
|
||||
regKey.valueExists(name, (e, exists) => {
|
||||
if (e) {
|
||||
return reject(e);
|
||||
}
|
||||
if (exists) {
|
||||
regKey.get(name, (err, result) => {
|
||||
if (!err) {
|
||||
return resolve(result ? result.value : '');
|
||||
} else {
|
||||
return reject(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return resolve('');
|
||||
}
|
||||
});
|
||||
} catch (ex) {
|
||||
return reject(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function directoryExistsSync(dirPath: string): boolean {
|
||||
|
@ -101,14 +113,14 @@ export function fileExistsSync(filePath: string): boolean {
|
|||
export function getScriptTemplateNameFromLanguage(language: string): string|
|
||||
undefined {
|
||||
switch (language) {
|
||||
case AzureFunctionsLanguage.CSharpScript:
|
||||
return 'IoTHubTrigger-CSharp';
|
||||
case AzureFunctionsLanguage.JavaScript:
|
||||
return 'IoTHubTrigger-JavaScript';
|
||||
case AzureFunctionsLanguage.CSharpLibrary:
|
||||
return 'Azure.Function.CSharp.IotHubTrigger.2.x';
|
||||
default:
|
||||
return undefined;
|
||||
case AzureFunctionsLanguage.CSharpScript:
|
||||
return 'IoTHubTrigger-CSharp';
|
||||
case AzureFunctionsLanguage.JavaScript:
|
||||
return 'IoTHubTrigger-JavaScript';
|
||||
case AzureFunctionsLanguage.CSharpLibrary:
|
||||
return 'Azure.Function.CSharp.IotHubTrigger.2.x';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +142,7 @@ export function getFirstWorkspaceFolderPath(showWarningMessage = true): string {
|
|||
!vscode.workspace.workspaceFolders[0].uri.fsPath) {
|
||||
if (showWarningMessage) {
|
||||
vscode.window.showWarningMessage(
|
||||
'You have not yet opened a folder in Visual Studio Code. Please select a folder first.');
|
||||
'You have not yet opened a folder in Visual Studio Code. Please select a folder first.');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
@ -138,24 +150,6 @@ export function getFirstWorkspaceFolderPath(showWarningMessage = true): string {
|
|||
return vscode.workspace.workspaceFolders[0].uri.fsPath;
|
||||
}
|
||||
|
||||
export async function selectWorkspaceFolder(
|
||||
placeHolder: string,
|
||||
getSubPath?: (f: vscode.WorkspaceFolder) =>
|
||||
string | undefined): Promise<string> {
|
||||
return await selectWorkspaceItem(
|
||||
placeHolder, {
|
||||
canSelectFiles: false,
|
||||
canSelectFolders: true,
|
||||
canSelectMany: false,
|
||||
defaultUri: vscode.workspace.workspaceFolders &&
|
||||
vscode.workspace.workspaceFolders.length > 0 ?
|
||||
vscode.workspace.workspaceFolders[0].uri :
|
||||
undefined,
|
||||
openLabel: 'Select'
|
||||
},
|
||||
getSubPath);
|
||||
}
|
||||
|
||||
export async function showOpenDialog(options: vscode.OpenDialogOptions):
|
||||
Promise<vscode.Uri[]> {
|
||||
const result: vscode.Uri[]|undefined =
|
||||
|
@ -169,37 +163,54 @@ export async function showOpenDialog(options: vscode.OpenDialogOptions):
|
|||
}
|
||||
|
||||
export async function selectWorkspaceItem(
|
||||
placeHolder: string, options: vscode.OpenDialogOptions,
|
||||
getSubPath?: (f: vscode.WorkspaceFolder) =>
|
||||
string | undefined): Promise<string> {
|
||||
let folder: FolderQuickPickItem<string|undefined>|undefined;
|
||||
placeHolder: string, options: vscode.OpenDialogOptions,
|
||||
getSubPath?: (f: vscode.WorkspaceFolder) =>
|
||||
string | undefined): Promise<string> {
|
||||
let folderPicks: Array<FolderQuickPickItem<string|undefined>> = [];
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
folderPicks =
|
||||
vscode.workspace.workspaceFolders.map((f: vscode.WorkspaceFolder) => {
|
||||
let subpath: string|undefined;
|
||||
if (getSubPath) {
|
||||
subpath = getSubPath(f);
|
||||
}
|
||||
vscode.workspace.workspaceFolders.map((f: vscode.WorkspaceFolder) => {
|
||||
let subpath: string|undefined;
|
||||
if (getSubPath) {
|
||||
subpath = getSubPath(f);
|
||||
}
|
||||
|
||||
const fsPath: string =
|
||||
subpath ? path.join(f.uri.fsPath, subpath) : f.uri.fsPath;
|
||||
return {
|
||||
label: path.basename(fsPath),
|
||||
description: fsPath,
|
||||
data: fsPath
|
||||
};
|
||||
});
|
||||
const fsPath: string =
|
||||
subpath ? path.join(f.uri.fsPath, subpath) : f.uri.fsPath;
|
||||
return {
|
||||
label: path.basename(fsPath),
|
||||
description: fsPath,
|
||||
data: fsPath
|
||||
};
|
||||
});
|
||||
}
|
||||
folderPicks.push({label: 'Browse...', description: '', data: undefined});
|
||||
folder = await vscode.window.showQuickPick(
|
||||
folderPicks, {placeHolder, ignoreFocusOut: true});
|
||||
folderPicks.push({ label: 'Browse...', description: '', data: undefined });
|
||||
const folder = await vscode.window.showQuickPick(
|
||||
folderPicks, { placeHolder, ignoreFocusOut: true });
|
||||
if (!folder) {
|
||||
throw new Error('User cancelled the operation.');
|
||||
}
|
||||
|
||||
return folder && folder.data ? folder.data :
|
||||
(await showOpenDialog(options))[0].fsPath;
|
||||
(await showOpenDialog(options))[0].fsPath;
|
||||
}
|
||||
|
||||
export async function selectWorkspaceFolder(
|
||||
placeHolder: string,
|
||||
getSubPath?: (f: vscode.WorkspaceFolder) =>
|
||||
string | undefined): Promise<string> {
|
||||
return await selectWorkspaceItem(
|
||||
placeHolder, {
|
||||
canSelectFiles: false,
|
||||
canSelectFolders: true,
|
||||
canSelectMany: false,
|
||||
defaultUri: vscode.workspace.workspaceFolders &&
|
||||
vscode.workspace.workspaceFolders.length > 0 ?
|
||||
vscode.workspace.workspaceFolders[0].uri :
|
||||
undefined,
|
||||
openLabel: 'Select'
|
||||
},
|
||||
getSubPath);
|
||||
}
|
||||
|
||||
export function executeCommand(command: string): Promise<string> {
|
||||
|
@ -217,12 +228,12 @@ export function executeCommand(command: string): Promise<string> {
|
|||
}
|
||||
|
||||
export function runCommand(
|
||||
command: string, args: string[], workingDir: string,
|
||||
outputChannel: vscode.OutputChannel): Thenable<object> {
|
||||
command: string, args: string[], workingDir: string,
|
||||
outputChannel: vscode.OutputChannel): Thenable<object> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const stdout = '';
|
||||
const stderr = '';
|
||||
const process = cp.spawn(command, args, {cwd: workingDir, shell: true});
|
||||
const process = cp.spawn(command, args, { cwd: workingDir, shell: true });
|
||||
process.stdout.on('data', (data: string) => {
|
||||
console.log(data);
|
||||
outputChannel.appendLine(data);
|
||||
|
@ -231,12 +242,12 @@ export function runCommand(
|
|||
console.log(data);
|
||||
outputChannel.appendLine(data);
|
||||
});
|
||||
process.on('error', error => reject({error, stderr, stdout}));
|
||||
process.on('error', error => reject({ error, stderr, stdout }));
|
||||
process.on('close', status => {
|
||||
if (status === 0) {
|
||||
resolve({status, stdout, stderr});
|
||||
resolve({ status, stdout, stderr });
|
||||
} else {
|
||||
reject({status, stdout, stderr});
|
||||
reject({ status, stdout, stderr });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -247,36 +258,36 @@ export function runCommand(
|
|||
* first.
|
||||
*/
|
||||
export async function askToConfigureEnvironment(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, platform: PlatformType,
|
||||
deviceRootPath: string, scaffoldType: ScaffoldType,
|
||||
operation: OperationType): Promise<void> {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, platform: PlatformType,
|
||||
deviceRootPath: string, scaffoldType: ScaffoldType,
|
||||
operation: OperationType): Promise<void> {
|
||||
telemetryContext.properties.result = TelemetryResult.Failed;
|
||||
|
||||
channelShowAndAppendLine(
|
||||
channel,
|
||||
`${operation} operation failed because the project environment needs configuring.`);
|
||||
channel,
|
||||
`${operation} operation failed because the project environment needs configuring.`);
|
||||
const message = `${
|
||||
operation} operation failed because the project environment needs configuring. Do you want to configure project environment first?`;
|
||||
operation} operation failed because the project environment needs configuring. Do you want to configure project environment first?`;
|
||||
const result: vscode.MessageItem|undefined =
|
||||
await vscode.window.showInformationMessage(
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
|
||||
if (result === DialogResponses.yes) {
|
||||
telemetryContext.properties.errorMessage = `${
|
||||
operation} operation failed and user configures project environment.`;
|
||||
operation} operation failed and user configures project environment.`;
|
||||
|
||||
await ProjectEnvironmentConfiger.configureProjectEnvironmentAsPlatform(
|
||||
context, channel, telemetryContext, platform, deviceRootPath,
|
||||
scaffoldType);
|
||||
context, channel, telemetryContext, platform, deviceRootPath,
|
||||
scaffoldType);
|
||||
const message =
|
||||
`Configuration of project environmnet done. You can run the ${
|
||||
operation.toLocaleLowerCase()} operation now.`;
|
||||
operation.toLocaleLowerCase()} operation now.`;
|
||||
channelShowAndAppendLine(channel, message);
|
||||
vscode.window.showInformationMessage(message);
|
||||
} else {
|
||||
const message = `${
|
||||
operation} operation failed and user cancels to configure project environment.`;
|
||||
operation} operation failed and user cancels to configure project environment.`;
|
||||
throw new CancelOperationError(message);
|
||||
}
|
||||
}
|
||||
|
@ -288,26 +299,26 @@ export async function askToConfigureEnvironment(
|
|||
* @param telemetryContext telemetry context
|
||||
*/
|
||||
export async function askAndOpenProject(
|
||||
rootPath: string, workspaceFile: string,
|
||||
telemetryContext: TelemetryContext): Promise<void> {
|
||||
rootPath: string, workspaceFile: string,
|
||||
telemetryContext: TelemetryContext): Promise<void> {
|
||||
telemetryContext.properties.result = TelemetryResult.Failed;
|
||||
|
||||
const message =
|
||||
`Operation failed because the IoT project is not opened. Current folder contains an IoT project '${
|
||||
workspaceFile}', do you want to open it?`;
|
||||
workspaceFile}', do you want to open it?`;
|
||||
const result: vscode.MessageItem|undefined =
|
||||
await vscode.window.showInformationMessage(
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
|
||||
if (result === DialogResponses.yes) {
|
||||
telemetryContext.properties.errorMessage =
|
||||
'Operation failed and user opens project folder as workspace.';
|
||||
const workspaceFilePath = path.join(rootPath, workspaceFile);
|
||||
await vscode.commands.executeCommand(
|
||||
IoTCubeCommands.OpenLocally, workspaceFilePath, false);
|
||||
IoTCubeCommands.OpenLocally, workspaceFilePath, false);
|
||||
} else {
|
||||
throw new CancelOperationError(
|
||||
`Operation failed and user cancels to open current folder as workspace.`);
|
||||
`Operation failed and user cancels to open current folder as workspace.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,14 +329,14 @@ export async function askAndOpenProject(
|
|||
* @param telemetryContext telemetry context
|
||||
*/
|
||||
export async function askAndOpenInRemote(
|
||||
operation: OperationType, telemetryContext: TelemetryContext) {
|
||||
operation: OperationType, telemetryContext: TelemetryContext): Promise<void> {
|
||||
telemetryContext.properties.result = TelemetryResult.Failed;
|
||||
|
||||
const message = `${
|
||||
operation} can only be executed in remote container. Do you want to reopen the IoT project in container?`;
|
||||
operation} can only be executed in remote container. Do you want to reopen the IoT project in container?`;
|
||||
const result: vscode.MessageItem|undefined =
|
||||
await vscode.window.showInformationMessage(
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
message, DialogResponses.yes, DialogResponses.no);
|
||||
|
||||
if (result === DialogResponses.yes) {
|
||||
telemetryContext.properties.errorMessage =
|
||||
|
@ -333,22 +344,22 @@ export async function askAndOpenInRemote(
|
|||
await RemoteExtension.checkRemoteExtension();
|
||||
|
||||
await vscode.commands.executeCommand(
|
||||
RemoteContainersCommands.ReopenInContainer);
|
||||
RemoteContainersCommands.ReopenInContainer);
|
||||
} else {
|
||||
throw new CancelOperationError(`${
|
||||
operation} operation failed and user cancels to reopen project in container.`);
|
||||
operation} operation failed and user cancels to reopen project in container.`);
|
||||
}
|
||||
}
|
||||
|
||||
const noDeviceSurveyUrl = 'https://www.surveymonkey.com/r/C7NY7KJ';
|
||||
|
||||
export async function takeNoDeviceSurvey(
|
||||
telemetryContext: TelemetryContext, context: vscode.ExtensionContext) {
|
||||
telemetryContext: TelemetryContext, context: vscode.ExtensionContext): Promise<void> {
|
||||
const message =
|
||||
'Could you help to take a quick survey about what IoT development kit(s) you want Azure IoT Device Workbench to support?';
|
||||
const result: vscode.MessageItem|undefined =
|
||||
await vscode.window.showWarningMessage(
|
||||
message, DialogResponses.yes, DialogResponses.cancel);
|
||||
message, DialogResponses.yes, DialogResponses.cancel);
|
||||
if (result === DialogResponses.yes) {
|
||||
// Open the survey page
|
||||
telemetryContext.properties.message = 'User takes no-device survey.';
|
||||
|
@ -360,10 +371,10 @@ export async function takeNoDeviceSurvey(
|
|||
}
|
||||
const extensionVersion = extension.packageJSON.version || 'unknown';
|
||||
await vscode.commands.executeCommand(
|
||||
VscodeCommands.VscodeOpen,
|
||||
vscode.Uri.parse(
|
||||
`${noDeviceSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${
|
||||
encodeURIComponent(extensionVersion)}`));
|
||||
VscodeCommands.VscodeOpen,
|
||||
vscode.Uri.parse(
|
||||
`${noDeviceSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${
|
||||
encodeURIComponent(extensionVersion)}`));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -388,8 +399,8 @@ export async function getTemplateFilesInfo(templateFolder: string):
|
|||
sourcePath: fileInfo.sourcePath,
|
||||
targetPath: fileInfo.targetPath,
|
||||
overwrite: typeof fileInfo.overwrite !== 'undefined' ?
|
||||
fileInfo.overwrite :
|
||||
true, // if it is not defined, we will overwrite the existing file.
|
||||
fileInfo.overwrite :
|
||||
true, // if it is not defined, we will overwrite the existing file.
|
||||
fileContent
|
||||
});
|
||||
});
|
||||
|
@ -398,7 +409,7 @@ export async function getTemplateFilesInfo(templateFolder: string):
|
|||
}
|
||||
|
||||
export async function generateTemplateFile(
|
||||
root: string, type: ScaffoldType, fileInfo: TemplateFileInfo) {
|
||||
root: string, type: ScaffoldType, fileInfo: TemplateFileInfo): Promise<void> {
|
||||
const targetFolderPath = path.join(root, fileInfo.targetPath);
|
||||
if (!(await FileUtility.directoryExists(type, targetFolderPath))) {
|
||||
await FileUtility.mkdirRecursively(type, targetFolderPath);
|
||||
|
@ -413,27 +424,15 @@ export async function generateTemplateFile(
|
|||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to create sketch file ${fileInfo.fileName}: ${
|
||||
error.message}`);
|
||||
error.message}`);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
export function channelShowAndAppend(
|
||||
channel: vscode.OutputChannel, message: string) {
|
||||
channel.show();
|
||||
channel.append(message);
|
||||
}
|
||||
|
||||
export function channelShowAndAppendLine(
|
||||
channel: vscode.OutputChannel, message: string) {
|
||||
channel.show();
|
||||
channel.appendLine(message);
|
||||
}
|
||||
|
||||
export function channelPrintJsonObject(
|
||||
// tslint:disable-next-line: no-any
|
||||
channel: vscode.OutputChannel, data: any) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
channel: vscode.OutputChannel, data: any): void {
|
||||
const indentationSpace = 4;
|
||||
const jsonString = JSON.stringify(data, null, indentationSpace);
|
||||
channelShowAndAppendLine(channel, jsonString);
|
||||
|
@ -444,32 +443,98 @@ export function channelPrintJsonObject(
|
|||
* Project or create an IoT Project
|
||||
*/
|
||||
export async function handleExternalProject(
|
||||
telemetryContext: TelemetryContext) {
|
||||
telemetryContext: TelemetryContext): Promise<void> {
|
||||
telemetryContext.properties.result = TelemetryResult.Failed;
|
||||
const message =
|
||||
'An IoT project is needed to process the operation, do you want to configure current project to be an IoT Embedded Linux Project or create an IoT project?';
|
||||
class Choice {
|
||||
static configureAsContainerProject:
|
||||
MessageItem = {title: 'Configure as Embedded Linux Project'};
|
||||
static createNewProject: MessageItem = {title: 'Create IoT Project'};
|
||||
MessageItem = { title: 'Configure as Embedded Linux Project' };
|
||||
static createNewProject: MessageItem = { title: 'Create IoT Project' };
|
||||
}
|
||||
|
||||
const result: vscode.MessageItem|undefined =
|
||||
await vscode.window.showInformationMessage(
|
||||
message, Choice.configureAsContainerProject, Choice.createNewProject);
|
||||
message, Choice.configureAsContainerProject, Choice.createNewProject);
|
||||
|
||||
if (result === Choice.configureAsContainerProject) {
|
||||
telemetryContext.properties.errorMessage =
|
||||
'Operation failed and user configures external project to be an IoT Embedded Linux Project';
|
||||
await vscode.commands.executeCommand(
|
||||
WorkbenchCommands.ConfigureProjectEnvironment);
|
||||
WorkbenchCommands.ConfigureProjectEnvironment);
|
||||
} else if (result === Choice.createNewProject) {
|
||||
telemetryContext.properties.errorMessage =
|
||||
'Operation failed and user creates new project';
|
||||
await vscode.commands.executeCommand(WorkbenchCommands.InitializeProject);
|
||||
} else {
|
||||
throw new CancelOperationError(
|
||||
`Operation failed and user cancels to configure external project.`);
|
||||
`Operation failed and user cancels to configure external project.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get project configs from iot workbench project file
|
||||
* @param type Scaffold type
|
||||
*/
|
||||
export async function getProjectConfig(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: ScaffoldType, iotWorkbenchProjectFilePath: string): Promise<any> {
|
||||
let projectConfig: {[key: string]: string} = {};
|
||||
if (await FileUtility.fileExists(type, iotWorkbenchProjectFilePath)) {
|
||||
const projectConfigContent =
|
||||
(await FileUtility.readFile(
|
||||
type, iotWorkbenchProjectFilePath, 'utf8') as string)
|
||||
.trim();
|
||||
if (projectConfigContent) {
|
||||
projectConfig = JSON.parse(projectConfigContent);
|
||||
}
|
||||
}
|
||||
return projectConfig;
|
||||
}
|
||||
|
||||
export function getWorkspaceFile(rootPath: string): string {
|
||||
const workspaceFiles = fs.readdirSync(rootPath).filter(
|
||||
file => path.extname(file).endsWith(FileNames.workspaceExtensionName));
|
||||
if (workspaceFiles && workspaceFiles.length >= 0) {
|
||||
return workspaceFiles[0];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update project host type configuration in iot workbench project file.
|
||||
* Create one if not exists.
|
||||
* @param type Scaffold type
|
||||
*/
|
||||
export async function updateProjectHostTypeConfig(
|
||||
type: ScaffoldType, iotWorkbenchProjectFilePath: string,
|
||||
projectHostType: ProjectHostType): Promise<void> {
|
||||
try {
|
||||
if (!iotWorkbenchProjectFilePath) {
|
||||
throw new Error(`Iot workbench project file path is empty.`);
|
||||
}
|
||||
|
||||
// Get original configs from config file
|
||||
const projectConfig =
|
||||
await getProjectConfig(type, iotWorkbenchProjectFilePath);
|
||||
|
||||
// Update project host type
|
||||
projectConfig[`${ConfigKey.projectHostType}`] =
|
||||
ProjectHostType[projectHostType];
|
||||
|
||||
// Add config version for easier backward compatibility in the future.
|
||||
const workbenchVersion = '1.0.0';
|
||||
if (!projectConfig[`${ConfigKey.workbenchVersion}`]) {
|
||||
projectConfig[`${ConfigKey.workbenchVersion}`] = workbenchVersion;
|
||||
}
|
||||
|
||||
await FileUtility.writeJsonFile(
|
||||
type, iotWorkbenchProjectFilePath, projectConfig);
|
||||
} catch (error) {
|
||||
throw new Error(`Update ${
|
||||
FileNames.iotWorkbenchProjectFileName} file failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,13 +545,13 @@ export async function handleExternalProject(
|
|||
* @param scaffoldType
|
||||
*/
|
||||
export async function configExternalCMakeProjectToIoTContainerProject(
|
||||
scaffoldType: ScaffoldType): Promise<void> {
|
||||
scaffoldType: ScaffoldType): Promise<void> {
|
||||
const projectRootPath = getFirstWorkspaceFolderPath();
|
||||
// Check if it is a cmake project
|
||||
const cmakeFile = path.join(projectRootPath, FileNames.cmakeFileName);
|
||||
if (!await FileUtility.fileExists(scaffoldType, cmakeFile)) {
|
||||
const message = `Missing ${
|
||||
FileNames.cmakeFileName} to be configured as Embedded Linux project.`;
|
||||
FileNames.cmakeFileName} to be configured as Embedded Linux project.`;
|
||||
vscode.window.showWarningMessage(message);
|
||||
throw new CancelOperationError(message);
|
||||
}
|
||||
|
@ -496,7 +561,7 @@ export async function configExternalCMakeProjectToIoTContainerProject(
|
|||
|
||||
// Update project host type in IoT Workbench Project file
|
||||
await updateProjectHostTypeConfig(
|
||||
scaffoldType, iotWorkbenchProjectFile, ProjectHostType.Container);
|
||||
scaffoldType, iotWorkbenchProjectFile, ProjectHostType.Container);
|
||||
|
||||
// Update board Id as Raspberry Pi in IoT Workbench Project file
|
||||
const projectConfig =
|
||||
|
@ -505,73 +570,7 @@ export async function configExternalCMakeProjectToIoTContainerProject(
|
|||
raspberryPiDeviceModule.RaspberryPiDevice.boardId;
|
||||
|
||||
await FileUtility.writeJsonFile(
|
||||
scaffoldType, iotWorkbenchProjectFile, projectConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update project host type configuration in iot workbench project file.
|
||||
* Create one if not exists.
|
||||
* @param type Scaffold type
|
||||
*/
|
||||
export async function updateProjectHostTypeConfig(
|
||||
type: ScaffoldType, iotWorkbenchProjectFilePath: string,
|
||||
projectHostType: ProjectHostType): Promise<void> {
|
||||
try {
|
||||
if (!iotWorkbenchProjectFilePath) {
|
||||
throw new Error(`Iot workbench project file path is empty.`);
|
||||
}
|
||||
|
||||
// Get original configs from config file
|
||||
const projectConfig =
|
||||
await getProjectConfig(type, iotWorkbenchProjectFilePath);
|
||||
|
||||
// Update project host type
|
||||
projectConfig[`${ConfigKey.projectHostType}`] =
|
||||
ProjectHostType[projectHostType];
|
||||
|
||||
// Add config version for easier backward compatibility in the future.
|
||||
const workbenchVersion = '1.0.0';
|
||||
if (!projectConfig[`${ConfigKey.workbenchVersion}`]) {
|
||||
projectConfig[`${ConfigKey.workbenchVersion}`] = workbenchVersion;
|
||||
}
|
||||
|
||||
await FileUtility.writeJsonFile(
|
||||
type, iotWorkbenchProjectFilePath, projectConfig);
|
||||
} catch (error) {
|
||||
throw new Error(`Update ${
|
||||
FileNames.iotWorkbenchProjectFileName} file failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get project configs from iot workbench project file
|
||||
* @param type Scaffold type
|
||||
*/
|
||||
export async function getProjectConfig(
|
||||
// tslint:disable-next-line: no-any
|
||||
type: ScaffoldType, iotWorkbenchProjectFilePath: string): Promise<any> {
|
||||
let projectConfig: {[key: string]: string} = {};
|
||||
if (await FileUtility.fileExists(type, iotWorkbenchProjectFilePath)) {
|
||||
const projectConfigContent =
|
||||
(await FileUtility.readFile(
|
||||
type, iotWorkbenchProjectFilePath, 'utf8') as string)
|
||||
.trim();
|
||||
if (projectConfigContent) {
|
||||
projectConfig = JSON.parse(projectConfigContent);
|
||||
}
|
||||
}
|
||||
return projectConfig;
|
||||
}
|
||||
|
||||
export function getWorkspaceFile(rootPath: string): string {
|
||||
const workspaceFiles = fs.readdirSync(rootPath).filter(
|
||||
file => path.extname(file).endsWith(FileNames.workspaceExtensionName));
|
||||
if (workspaceFiles && workspaceFiles.length >= 0) {
|
||||
return workspaceFiles[0];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
scaffoldType, iotWorkbenchProjectFile, projectConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -579,7 +578,7 @@ export function getWorkspaceFile(rootPath: string): string {
|
|||
* Ask to open as workspace.
|
||||
*/
|
||||
export async function properlyOpenIoTWorkspaceProject(
|
||||
telemetryContext: TelemetryContext): Promise<void> {
|
||||
telemetryContext: TelemetryContext): Promise<void> {
|
||||
const rootPath = getFirstWorkspaceFolderPath();
|
||||
const workbenchFileName =
|
||||
path.join(rootPath, 'Device', FileNames.iotWorkbenchProjectFileName);
|
||||
|
@ -607,22 +606,22 @@ export function isWorkspaceProject(): boolean {
|
|||
* check project validation and throw error if any.
|
||||
*/
|
||||
export async function constructAndLoadIoTProject(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, isTriggeredWhenExtensionLoad = false) {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, isTriggeredWhenExtensionLoad = false): Promise<IoTWorkbenchProjectBase|undefined> {
|
||||
const scaffoldType = ScaffoldType.Workspace;
|
||||
|
||||
const projectFileRootPath = getFirstWorkspaceFolderPath(false);
|
||||
const projectHostType = await IoTWorkbenchProjectBase.getProjectType(
|
||||
scaffoldType, projectFileRootPath);
|
||||
scaffoldType, projectFileRootPath);
|
||||
|
||||
let iotProject;
|
||||
if (projectHostType === ProjectHostType.Container) {
|
||||
iotProject = new ioTContainerizedProjectModule.IoTContainerizedProject(
|
||||
context, channel, telemetryContext, projectFileRootPath);
|
||||
context, channel, telemetryContext, projectFileRootPath);
|
||||
} else if (projectHostType === ProjectHostType.Workspace) {
|
||||
const projectRootPath = path.join(projectFileRootPath, '..');
|
||||
iotProject = new ioTWorkspaceProjectModule.IoTWorkspaceProject(
|
||||
context, channel, telemetryContext, projectRootPath);
|
||||
context, channel, telemetryContext, projectRootPath);
|
||||
}
|
||||
|
||||
if (isTriggeredWhenExtensionLoad) {
|
||||
|
@ -652,7 +651,7 @@ export async function constructAndLoadIoTProject(
|
|||
// Ignore if user cancel operation
|
||||
if (!(err instanceof CancelOperationError)) {
|
||||
throw new Error(
|
||||
`Failed to handle external project. Error: ${err.message}`);
|
||||
`Failed to handle external project. Error: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -664,9 +663,9 @@ export async function constructAndLoadIoTProject(
|
|||
return iotProject;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
export function getEnumKeyByEnumValue(myEnum: any, enumValue: any) {
|
||||
// tslint:disable-next-line: no-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function getEnumKeyByEnumValue(myEnum: any, enumValue: any): any {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const keys = Object.keys(myEnum).filter(x => myEnum[x] === enumValue);
|
||||
const key = keys.length > 0 ? keys[0] : null;
|
||||
if (key === null) {
|
||||
|
@ -676,11 +675,11 @@ export function getEnumKeyByEnumValue(myEnum: any, enumValue: any) {
|
|||
}
|
||||
|
||||
export async function selectPlatform(
|
||||
type: ScaffoldType,
|
||||
context: vscode.ExtensionContext): Promise<vscode.QuickPickItem|undefined> {
|
||||
type: ScaffoldType,
|
||||
context: vscode.ExtensionContext): Promise<vscode.QuickPickItem|undefined> {
|
||||
const platformListPath = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.platformListFileName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.platformListFileName));
|
||||
const platformListJsonString =
|
||||
await FileUtility.readFile(type, platformListPath, 'utf8') as string;
|
||||
const platformListJson = JSON.parse(platformListJsonString);
|
||||
|
@ -693,7 +692,7 @@ export async function selectPlatform(
|
|||
|
||||
platformListJson.platforms.forEach((platform: Platform) => {
|
||||
platformList.push(
|
||||
{label: platform.name, description: platform.description});
|
||||
{ label: platform.name, description: platform.description });
|
||||
});
|
||||
|
||||
const platformSelection = await vscode.window.showQuickPick(platformList, {
|
||||
|
@ -711,6 +710,40 @@ enum OverwriteLabel {
|
|||
No = 'No',
|
||||
YesToAll = 'Yes to all'
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask whether to overwrite all configuration files
|
||||
*/
|
||||
export async function askToOverwriteFile(fileName: string):
|
||||
Promise<vscode.QuickPickItem> {
|
||||
const overwriteTasksJsonOption: vscode.QuickPickItem[] = [];
|
||||
overwriteTasksJsonOption.push(
|
||||
{
|
||||
label: OverwriteLabel.No,
|
||||
detail:
|
||||
'Do not overwrite existed file and cancel the configuration process.'
|
||||
},
|
||||
{
|
||||
label: OverwriteLabel.YesToAll,
|
||||
detail: 'Automatically overwrite all configuration files.'
|
||||
});
|
||||
|
||||
const overwriteSelection =
|
||||
await vscode.window.showQuickPick(overwriteTasksJsonOption, {
|
||||
ignoreFocusOut: true,
|
||||
placeHolder: `Configuration file ${
|
||||
fileName} already exists. Do you want to overwrite all existed configuration files or cancel the configuration process?`
|
||||
});
|
||||
|
||||
if (!overwriteSelection) {
|
||||
// Selection was cancelled
|
||||
throw new CancelOperationError(
|
||||
`Ask to overwrite ${fileName} selection cancelled.`);
|
||||
}
|
||||
|
||||
return overwriteSelection;
|
||||
}
|
||||
|
||||
/**
|
||||
* If one of any configuration files already exists, ask to overwrite all or
|
||||
* cancel configuration process.
|
||||
|
@ -718,8 +751,8 @@ enum OverwriteLabel {
|
|||
* configuration process.
|
||||
*/
|
||||
export async function askToOverwrite(
|
||||
scaffoldType: ScaffoldType, projectPath: string,
|
||||
templateFilesInfo: TemplateFileInfo[]): Promise<boolean> {
|
||||
scaffoldType: ScaffoldType, projectPath: string,
|
||||
templateFilesInfo: TemplateFileInfo[]): Promise<boolean> {
|
||||
// Check whether configuration file exists
|
||||
for (const fileInfo of templateFilesInfo) {
|
||||
const targetFilePath =
|
||||
|
@ -734,44 +767,12 @@ export async function askToOverwrite(
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask whether to overwrite all configuration files
|
||||
*/
|
||||
export async function askToOverwriteFile(fileName: string):
|
||||
Promise<vscode.QuickPickItem> {
|
||||
const overwriteTasksJsonOption: vscode.QuickPickItem[] = [];
|
||||
overwriteTasksJsonOption.push(
|
||||
{
|
||||
label: OverwriteLabel.No,
|
||||
detail:
|
||||
'Do not overwrite existed file and cancel the configuration process.'
|
||||
},
|
||||
{
|
||||
label: OverwriteLabel.YesToAll,
|
||||
detail: 'Automatically overwrite all configuration files.'
|
||||
});
|
||||
|
||||
const overwriteSelection =
|
||||
await vscode.window.showQuickPick(overwriteTasksJsonOption, {
|
||||
ignoreFocusOut: true,
|
||||
placeHolder: `Configuration file ${
|
||||
fileName} already exists. Do you want to overwrite all existed configuration files or cancel the configuration process?`
|
||||
});
|
||||
|
||||
if (!overwriteSelection) {
|
||||
// Selection was cancelled
|
||||
throw new CancelOperationError(
|
||||
`Ask to overwrite ${fileName} selection cancelled.`);
|
||||
}
|
||||
|
||||
return overwriteSelection;
|
||||
}
|
||||
|
||||
export async function fetchAndExecuteTask(
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, deviceRootPath: string,
|
||||
operationType: OperationType, platform: PlatformType,
|
||||
taskName: string): Promise<void> {
|
||||
context: vscode.ExtensionContext, channel: vscode.OutputChannel,
|
||||
telemetryContext: TelemetryContext, deviceRootPath: string,
|
||||
operationType: OperationType, platform: PlatformType,
|
||||
taskName: string): Promise<void> {
|
||||
const scaffoldType = ScaffoldType.Workspace;
|
||||
if (!await FileUtility.directoryExists(scaffoldType, deviceRootPath)) {
|
||||
throw new Error('Unable to find the device root folder.');
|
||||
|
@ -783,8 +784,8 @@ export async function fetchAndExecuteTask(
|
|||
channelShowAndAppendLine(channel, message);
|
||||
|
||||
await askToConfigureEnvironment(
|
||||
context, channel, telemetryContext, platform, deviceRootPath,
|
||||
scaffoldType, operationType);
|
||||
context, channel, telemetryContext, platform, deviceRootPath,
|
||||
scaffoldType, operationType);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -793,12 +794,12 @@ export async function fetchAndExecuteTask(
|
|||
});
|
||||
if (!operationTask || operationTask.length < 1) {
|
||||
const message = `Failed to fetch default ${
|
||||
operationType.toLowerCase()} task with task name ${taskName}.`;
|
||||
operationType.toLowerCase()} task with task name ${taskName}.`;
|
||||
channelShowAndAppendLine(channel, message);
|
||||
|
||||
await askToConfigureEnvironment(
|
||||
context, channel, telemetryContext, platform, deviceRootPath,
|
||||
scaffoldType, operationType);
|
||||
context, channel, telemetryContext, platform, deviceRootPath,
|
||||
scaffoldType, operationType);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -806,7 +807,7 @@ export async function fetchAndExecuteTask(
|
|||
await vscode.tasks.executeTask(operationTask[0]);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to execute task to ${
|
||||
operationType.toLowerCase()}: ${error.message}`);
|
||||
operationType.toLowerCase()}: ${error.message}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -816,18 +817,18 @@ export async function fetchAndExecuteTask(
|
|||
* overwrite files if any exists
|
||||
*/
|
||||
export async function getEnvTemplateFilesAndAskOverwrite(
|
||||
context: vscode.ExtensionContext, projectPath: string,
|
||||
scaffoldType: ScaffoldType,
|
||||
templateName: string): Promise<TemplateFileInfo[]> {
|
||||
context: vscode.ExtensionContext, projectPath: string,
|
||||
scaffoldType: ScaffoldType,
|
||||
templateName: string): Promise<TemplateFileInfo[]> {
|
||||
if (!projectPath) {
|
||||
throw new Error(
|
||||
'Unable to find the project path, please open the folder and initialize project again.');
|
||||
'Unable to find the project path, please open the folder and initialize project again.');
|
||||
}
|
||||
|
||||
// Get template list json object
|
||||
const templateJsonFilePath = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.templateFileName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
FileNames.templateFileName));
|
||||
const templateJsonFileString =
|
||||
await FileUtility.readFile(scaffoldType, templateJsonFilePath, 'utf8') as
|
||||
string;
|
||||
|
@ -840,17 +841,17 @@ export async function getEnvTemplateFilesAndAskOverwrite(
|
|||
const projectEnvTemplate: ProjectTemplate[] =
|
||||
templateJson.templates.filter((template: ProjectTemplate) => {
|
||||
return (
|
||||
template.tag === TemplateTag.DevelopmentEnvironment &&
|
||||
template.tag === TemplateTag.DevelopmentEnvironment &&
|
||||
template.name === templateName);
|
||||
});
|
||||
if (projectEnvTemplate.length === 0) {
|
||||
throw new Error(
|
||||
`Fail to get project development environment template files.`);
|
||||
`Fail to get project development environment template files.`);
|
||||
}
|
||||
const templateFolderName = projectEnvTemplate[0].path;
|
||||
const templateFolder = context.asAbsolutePath(path.join(
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
templateFolderName));
|
||||
FileNames.resourcesFolderName, FileNames.templatesFolderName,
|
||||
templateFolderName));
|
||||
const templateFilesInfo: TemplateFileInfo[] =
|
||||
await getTemplateFilesInfo(templateFolder);
|
||||
|
||||
|
@ -899,7 +900,7 @@ export function shouldShowLandingPage(context: vscode.ExtensionContext):
|
|||
* @param algorithm hash algorithm
|
||||
*/
|
||||
export function getHashFromString(
|
||||
stringToHash: string, algorithm = 'md5'): string {
|
||||
stringToHash: string, algorithm = 'md5'): string {
|
||||
const hash = crypto.createHash(algorithm);
|
||||
hash.update(stringToHash);
|
||||
const hashValue = hash.digest('hex');
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import * as assert from 'assert';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {AZ3166Device} from '../src/Models/AZ3166Device';
|
||||
import {ComponentType} from '../src/Models/Interfaces/Component';
|
||||
import {DeviceType} from '../src/Models/Interfaces/Device';
|
||||
import {TelemetryContext, TelemetryWorker} from '../src/telemetry';
|
||||
import { AZ3166Device } from '../src/Models/AZ3166Device';
|
||||
import { ComponentType } from '../src/Models/Interfaces/Component';
|
||||
import { DeviceType } from '../src/Models/Interfaces/Device';
|
||||
import { TelemetryContext, TelemetryWorker } from '../src/telemetry';
|
||||
|
||||
import {TestExtensionContext} from './stub';
|
||||
import { TestExtensionContext } from './stub';
|
||||
|
||||
suite('IoT Device Workbench: Device', () => {
|
||||
// tslint:disable-next-line: only-arrow-functions
|
||||
|
@ -16,7 +16,7 @@ suite('IoT Device Workbench: Device', () => {
|
|||
const telemetryWorker = TelemetryWorker.getInstance(context);
|
||||
const telemetryContext: TelemetryContext = telemetryWorker.createContext();
|
||||
const device = new AZ3166Device(context, channel, telemetryContext, '', []);
|
||||
assert.equal(device.getDeviceType(), DeviceType.MXChip_AZ3166);
|
||||
assert.equal(device.getDeviceType(), DeviceType.MXChipAZ3166);
|
||||
assert.equal(device.getComponentType(), ComponentType.Device);
|
||||
done();
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// Please refer to their documentation on https://mochajs.org/ for help.
|
||||
//
|
||||
import * as vscode from 'vscode';
|
||||
import {WorkbenchCommands} from '../src/common/Commands';
|
||||
import { WorkbenchCommands } from '../src/common/Commands';
|
||||
|
||||
suite('IoT Device Workbench: Commands Tests', () => {
|
||||
// tslint:disable-next-line: only-arrow-functions
|
||||
|
@ -16,12 +16,13 @@ suite('IoT Device Workbench: Commands Tests', () => {
|
|||
done('Failed to activate extension');
|
||||
} else if (!extension.isActive) {
|
||||
extension.activate().then(
|
||||
(api) => {
|
||||
done();
|
||||
},
|
||||
() => {
|
||||
done('Failed to activate extension');
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
(_api) => {
|
||||
done();
|
||||
},
|
||||
() => {
|
||||
done('Failed to activate extension');
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
|
@ -29,25 +30,25 @@ suite('IoT Device Workbench: Commands Tests', () => {
|
|||
|
||||
// tslint:disable-next-line: only-arrow-functions
|
||||
test(
|
||||
'should be able to run command: iotworkbench.exampleInitialize',
|
||||
function(done) {
|
||||
this.timeout(60 * 1000);
|
||||
try {
|
||||
vscode.commands.executeCommand(WorkbenchCommands.InitializeProject)
|
||||
.then((result) => {
|
||||
done();
|
||||
});
|
||||
'should be able to run command: iotworkbench.exampleInitialize',
|
||||
function(done) {
|
||||
this.timeout(60 * 1000);
|
||||
try {
|
||||
vscode.commands.executeCommand(WorkbenchCommands.InitializeProject)
|
||||
.then(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
done(new Error(error));
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
done(new Error(error));
|
||||
}
|
||||
});
|
||||
|
||||
// tslint:disable-next-line: only-arrow-functions
|
||||
test('should be able to run command: iotworkbench.help', function(done) {
|
||||
this.timeout(60 * 1000);
|
||||
try {
|
||||
vscode.commands.executeCommand(WorkbenchCommands.Help).then((result) => {
|
||||
vscode.commands.executeCommand(WorkbenchCommands.Help).then(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import * as assert from 'assert';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {ConfigHandler} from '../src/configHandler';
|
||||
import {ConfigKey} from '../src/constants';
|
||||
import { ConfigHandler } from '../src/configHandler';
|
||||
import { ConfigKey } from '../src/constants';
|
||||
|
||||
suite('IoT Device Workbench: Config', () => {
|
||||
test('should set and get config value correctly', async function() {
|
||||
|
|
|
@ -9,13 +9,13 @@ import * as assert from 'assert';
|
|||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from 'vscode';
|
||||
import * as myExtension from '../src/extension';
|
||||
|
||||
|
||||
// Defines a Mocha test suite to group tests of similar kind together
|
||||
suite('IoT Device Workbench Tests', () => {
|
||||
test('should be present', () => {
|
||||
assert.ok(
|
||||
vscode.extensions.getExtension('vsciot-vscode.vscode-iot-workbench'));
|
||||
vscode.extensions.getExtension('vsciot-vscode.vscode-iot-workbench'));
|
||||
});
|
||||
|
||||
// tslint:disable-next-line:only-arrow-functions
|
||||
|
@ -27,12 +27,12 @@ suite('IoT Device Workbench Tests', () => {
|
|||
done('Failed to activate extension');
|
||||
} else if (!extension.isActive) {
|
||||
extension.activate().then(
|
||||
(api) => {
|
||||
done();
|
||||
},
|
||||
() => {
|
||||
done('Failed to activate extension');
|
||||
});
|
||||
() => {
|
||||
done();
|
||||
},
|
||||
() => {
|
||||
done('Failed to activate extension');
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import * as testRunner from 'vscode/lib/testrunner';
|
|||
// for more info
|
||||
testRunner.configure({
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test,
|
||||
// etc.)
|
||||
// etc.)
|
||||
useColors: true // colored output from test results
|
||||
});
|
||||
|
||||
|
|
12
test/stub.ts
12
test/stub.ts
|
@ -1,12 +1,13 @@
|
|||
import * as vscode from 'vscode';
|
||||
|
||||
class DummyMemento implements vscode.Memento {
|
||||
get<T>(key: string): Promise<T|undefined> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
get<T>(_key: string): Promise<T|undefined> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
update(key: string, value: any): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
||||
update(_key: string, _value: any): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +22,11 @@ class TestExtensionContext implements vscode.ExtensionContext {
|
|||
storagePath = '';
|
||||
logPath = '';
|
||||
|
||||
asAbsolutePath(relativePath: string): string {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
asAbsolutePath(_relativePath: string): string {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {DummyMemento, TestExtensionContext};
|
||||
export { DummyMemento, TestExtensionContext };
|
|
@ -10,7 +10,10 @@
|
|||
"es2017"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "."
|
||||
"rootDir": ".",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"strict": true // Enable several strict checks
|
||||
},
|
||||
"include": [
|
||||
"src/*.ts",
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
{
|
||||
"extends": [
|
||||
"standard"
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"require-path-exists"
|
||||
"@typescript-eslint/eslint-plugin"
|
||||
],
|
||||
"env": {
|
||||
"node": true,
|
||||
|
@ -20,7 +21,6 @@
|
|||
"require-path-exists/notEmpty": 2,
|
||||
"require-path-exists/tooManyArguments": 2,
|
||||
"semi": [2, "always", {"omitLastInOneLineBlock": true}],
|
||||
"space-before-function-paren": [2, "never"],
|
||||
"standard/object-curly-even-spacing": 0
|
||||
"space-before-function-paren": [2, "never"]
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
'use strict';
|
||||
const path = require('path');
|
||||
const bindings = require('./native_loader').load(path.join(__dirname, 'native'), 'usb-native');
|
||||
var listUnix = require('./list-unix');
|
||||
const listUnix = require('./list-unix');
|
||||
|
||||
var linux = process.platform !== 'win32' && process.platform !== 'darwin';
|
||||
const linux = process.platform !== 'win32' && process.platform !== 'darwin';
|
||||
|
||||
function listLinux(callback) {
|
||||
callback = callback || function(err) {
|
||||
|
@ -14,7 +14,7 @@ function listLinux(callback) {
|
|||
return listUnix(callback);
|
||||
}
|
||||
|
||||
var platformOptions = {};
|
||||
let platformOptions = {};
|
||||
if (process.platform !== 'win32') {
|
||||
platformOptions = {
|
||||
vmin: 1,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var detection = require('./bindings');
|
||||
var EventEmitter2 = require('eventemitter2').EventEmitter2;
|
||||
const detection = require('./bindings');
|
||||
const EventEmitter2 = require('eventemitter2').EventEmitter2;
|
||||
|
||||
var detector = new EventEmitter2({
|
||||
const detector = new EventEmitter2({
|
||||
wildcard: true,
|
||||
delimiter: ':',
|
||||
maxListeners: 1000 // default would be 10!
|
||||
|
@ -20,7 +20,7 @@ detector.find = (vid, pid, callback) => {
|
|||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Assemble the optional args into something we can use with `apply`
|
||||
var args = [];
|
||||
let args = [];
|
||||
if (vid) {
|
||||
args = args.concat(vid);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ if (detection.registerAdded) {
|
|||
detector.emit('change', device);
|
||||
});
|
||||
|
||||
var started = true;
|
||||
let started = true;
|
||||
|
||||
detector.startMonitoring = () => {
|
||||
if (started) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
var childProcess = require('child_process');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
const childProcess = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function promisify(func) {
|
||||
return (arg) => {
|
||||
|
@ -19,7 +19,7 @@ function promisify(func) {
|
|||
|
||||
function promisedFilter(func) {
|
||||
return (data) => {
|
||||
var shouldKeep = data.map(func);
|
||||
const shouldKeep = data.map(func);
|
||||
return Promise.all(shouldKeep).then((keep) => {
|
||||
return data.filter((path, index) => {
|
||||
return keep[index];
|
||||
|
@ -28,16 +28,16 @@ function promisedFilter(func) {
|
|||
};
|
||||
}
|
||||
|
||||
var statAsync = promisify(fs.stat);
|
||||
var readdirAsync = promisify(fs.readdir);
|
||||
var execAsync = promisify(childProcess.exec);
|
||||
const statAsync = promisify(fs.stat);
|
||||
const readdirAsync = promisify(fs.readdir);
|
||||
const execAsync = promisify(childProcess.exec);
|
||||
|
||||
function udevParser(output) {
|
||||
var udevInfo = output.split('\n').reduce((info, line) => {
|
||||
const udevInfo = output.split('\n').reduce((info, line) => {
|
||||
if (!line || line.trim() === '') {
|
||||
return info;
|
||||
}
|
||||
var parts = line.split('=').map((part) => {
|
||||
const parts = line.split('=').map((part) => {
|
||||
return part.trim();
|
||||
});
|
||||
|
||||
|
@ -46,7 +46,7 @@ function udevParser(output) {
|
|||
return info;
|
||||
}, {});
|
||||
|
||||
var pnpId;
|
||||
let pnpId;
|
||||
if (udevInfo.devlinks) {
|
||||
udevInfo.devlinks.split(' ').forEach((path) => {
|
||||
if (path.indexOf('/by-id/') === -1) { return }
|
||||
|
@ -54,12 +54,12 @@ function udevParser(output) {
|
|||
});
|
||||
}
|
||||
|
||||
var vendorId = udevInfo.id_vendor_id;
|
||||
let vendorId = udevInfo.id_vendor_id;
|
||||
if (vendorId && vendorId.substring(0, 2) !== '0x') {
|
||||
vendorId = `0x${vendorId}`;
|
||||
}
|
||||
|
||||
var productId = udevInfo.id_model_id;
|
||||
let productId = udevInfo.id_model_id;
|
||||
if (productId && productId.substring(0, 2) !== '0x') {
|
||||
productId = `0x${productId}`;
|
||||
}
|
||||
|
@ -85,12 +85,12 @@ function checkPathAndDevice(path) {
|
|||
}
|
||||
|
||||
function lookupPort(file) {
|
||||
var udevadm = `udevadm info --query=property -p $(udevadm info -q path -n ${file})`;
|
||||
const udevadm = `udevadm info --query=property -p $(udevadm info -q path -n ${file})`;
|
||||
return execAsync(udevadm).then(udevParser);
|
||||
}
|
||||
|
||||
function listUnix(callback) {
|
||||
var dirName = '/dev';
|
||||
const dirName = '/dev';
|
||||
readdirAsync(dirName)
|
||||
.catch((err) => {
|
||||
// if this directory is not found we just pretend everything is OK
|
||||
|
|
|
@ -7,10 +7,10 @@ const loadLibrary = function(parentFolder, libraryName) {
|
|||
const nodepregypFiles = glob(`${parentFolder.replace(/\\/g, '/')}/${libraryName}*${process.arch}*.node`, {
|
||||
sync: true
|
||||
});
|
||||
var binding = null;
|
||||
let binding = null;
|
||||
nodegypFiles.concat(nodepregypFiles).forEach((file) => {
|
||||
try {
|
||||
var _temp = require(file);
|
||||
const _temp = require(file);
|
||||
binding = _temp;
|
||||
console.log('using', file);
|
||||
} catch (e) {
|
||||
|
|
|
@ -13,12 +13,12 @@ module.exports = {
|
|||
if (typeof delimiter === 'undefined' || delimiter === null) { delimiter = '\r' }
|
||||
if (typeof encoding === 'undefined' || encoding === null) { encoding = 'utf8' }
|
||||
// Delimiter buffer saved in closure
|
||||
var data = '';
|
||||
let data = '';
|
||||
return function(emitter, buffer) {
|
||||
// Collect data
|
||||
data += buffer.toString(encoding);
|
||||
// Split collected data by delimiter
|
||||
var parts = data.split(delimiter);
|
||||
const parts = data.split(delimiter);
|
||||
data = parts.pop();
|
||||
parts.forEach((part) => {
|
||||
emitter.emit('data', part);
|
||||
|
@ -28,11 +28,11 @@ module.exports = {
|
|||
|
||||
// Emit a data event every `length` bytes
|
||||
byteLength: function(length) {
|
||||
var data = Buffer.alloc(0);
|
||||
let data = Buffer.alloc(0);
|
||||
return function(emitter, buffer) {
|
||||
data = Buffer.concat([data, buffer]);
|
||||
while (data.length >= length) {
|
||||
var out = data.slice(0, length);
|
||||
const out = data.slice(0, length);
|
||||
data = data.slice(length);
|
||||
emitter.emit('data', out);
|
||||
}
|
||||
|
@ -45,10 +45,10 @@ module.exports = {
|
|||
if (Object.prototype.toString.call(delimiter) !== '[object Array]') {
|
||||
delimiter = [ delimiter ];
|
||||
}
|
||||
var buf = [];
|
||||
var nextDelimIndex = 0;
|
||||
let buf = [];
|
||||
let nextDelimIndex = 0;
|
||||
return function(emitter, buffer) {
|
||||
for (var i = 0; i < buffer.length; i++) {
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
buf[buf.length] = buffer[i];
|
||||
if (buf[buf.length - 1] === delimiter[nextDelimIndex]) {
|
||||
nextDelimIndex++;
|
||||
|
|
|
@ -9,26 +9,26 @@ const debug = (message) => {
|
|||
|
||||
// shims
|
||||
// Internal Dependencies
|
||||
var SerialPortBinding = require('./bindings');
|
||||
var parsers = require('./parsers');
|
||||
const SerialPortBinding = require('./bindings');
|
||||
const parsers = require('./parsers');
|
||||
|
||||
// Built-ins Dependencies
|
||||
var fs = require('fs');
|
||||
var stream = require('stream');
|
||||
var util = require('util');
|
||||
const fs = require('fs');
|
||||
const stream = require('stream');
|
||||
const util = require('util');
|
||||
|
||||
// VALIDATION ARRAYS
|
||||
var DATABITS = [5, 6, 7, 8];
|
||||
var STOPBITS = [1, 1.5, 2];
|
||||
var PARITY = ['none', 'even', 'mark', 'odd', 'space'];
|
||||
var FLOWCONTROLS = ['xon', 'xoff', 'xany', 'rtscts'];
|
||||
var SET_OPTIONS = ['brk', 'cts', 'dtr', 'dts', 'rts'];
|
||||
const DATABITS = [5, 6, 7, 8];
|
||||
const STOPBITS = [1, 1.5, 2];
|
||||
const PARITY = ['none', 'even', 'mark', 'odd', 'space'];
|
||||
const FLOWCONTROLS = ['xon', 'xoff', 'xany', 'rtscts'];
|
||||
const SET_OPTIONS = ['brk', 'cts', 'dtr', 'dts', 'rts'];
|
||||
|
||||
// Stuff from ReadStream, refactored for our usage:
|
||||
var kPoolSize = 40 * 1024;
|
||||
var kMinPoolSpace = 128;
|
||||
const kPoolSize = 40 * 1024;
|
||||
const kMinPoolSpace = 128;
|
||||
|
||||
var defaultSettings = {
|
||||
const defaultSettings = {
|
||||
baudRate: 9600,
|
||||
autoOpen: true,
|
||||
parity: 'none',
|
||||
|
@ -45,7 +45,7 @@ var defaultSettings = {
|
|||
platformOptions: SerialPortBinding.platformOptions
|
||||
};
|
||||
|
||||
var defaultSetFlags = {
|
||||
const defaultSetFlags = {
|
||||
brk: false,
|
||||
cts: false,
|
||||
dtr: true,
|
||||
|
@ -54,7 +54,7 @@ var defaultSetFlags = {
|
|||
};
|
||||
|
||||
// deprecate the lowercase version of these options next major release
|
||||
var LOWERCASE_OPTIONS = [
|
||||
const LOWERCASE_OPTIONS = [
|
||||
'baudRate',
|
||||
'dataBits',
|
||||
'stopBits',
|
||||
|
@ -64,9 +64,9 @@ var LOWERCASE_OPTIONS = [
|
|||
|
||||
function correctOptions(options) {
|
||||
LOWERCASE_OPTIONS.forEach((name) => {
|
||||
var lowerName = name.toLowerCase();
|
||||
const lowerName = name.toLowerCase();
|
||||
if (options.hasOwnProperty(lowerName)) {
|
||||
var value = options[lowerName];
|
||||
const value = options[lowerName];
|
||||
delete options[lowerName];
|
||||
options[name] = value;
|
||||
}
|
||||
|
@ -94,8 +94,8 @@ function SerialPort(path, options, callback) {
|
|||
|
||||
this.path = path;
|
||||
|
||||
var correctedOptions = correctOptions(options);
|
||||
var settings = Object.assign({}, defaultSettings, correctedOptions);
|
||||
const correctedOptions = correctOptions(options);
|
||||
const settings = Object.assign({}, defaultSettings, correctedOptions);
|
||||
|
||||
if (typeof settings.baudRate !== 'number') {
|
||||
throw new TypeError(`Invalid "baudRate" must be a number got: ${settings.baudRate}`);
|
||||
|
@ -198,8 +198,8 @@ SerialPort.prototype.update = function(options, callback) {
|
|||
return this._error(new Error('Port is not open'), callback);
|
||||
}
|
||||
|
||||
var correctedOptions = correctOptions(options);
|
||||
var settings = Object.assign({}, defaultSettings, correctedOptions);
|
||||
const correctedOptions = correctOptions(options);
|
||||
const settings = Object.assign({}, defaultSettings, correctedOptions);
|
||||
this.options.baudRate = settings.baudRate;
|
||||
|
||||
SerialPortBinding.update(this.fd, this.options, (err) => {
|
||||
|
@ -256,10 +256,10 @@ if (process.platform !== 'win32') {
|
|||
// Grab another reference to the pool in the case that while we're in the
|
||||
// thread pool another read() finishes up the pool, and allocates a new
|
||||
// one.
|
||||
var toRead = Math.min(this.pool.length - this.pool.used, ~~this.bufferSize);
|
||||
var start = this.pool.used;
|
||||
const toRead = Math.min(this.pool.length - this.pool.used, ~~this.bufferSize);
|
||||
const start = this.pool.used;
|
||||
|
||||
var _afterRead = (err, bytesRead, readPool, bytesRequested) => {
|
||||
const _afterRead = (err, bytesRead, readPool, bytesRequested) => {
|
||||
this.reading = false;
|
||||
if (err) {
|
||||
if (err.code && err.code === 'EAGAIN') {
|
||||
|
@ -286,7 +286,7 @@ if (process.platform !== 'win32') {
|
|||
this.serialPoller.start();
|
||||
}
|
||||
} else {
|
||||
var b = this.pool.slice(start, start + bytesRead);
|
||||
const b = this.pool.slice(start, start + bytesRead);
|
||||
|
||||
// do not emit events if the stream is paused
|
||||
if (this.paused) {
|
||||
|
@ -307,8 +307,8 @@ if (process.platform !== 'win32') {
|
|||
};
|
||||
|
||||
fs.read(this.fd, this.pool, this.pool.used, toRead, null, (err, bytesRead) => {
|
||||
var readPool = this.pool;
|
||||
var bytesRequested = toRead;
|
||||
const readPool = this.pool;
|
||||
const bytesRequested = toRead;
|
||||
_afterRead(err, bytesRead, readPool, bytesRequested);
|
||||
});
|
||||
|
||||
|
@ -327,7 +327,7 @@ if (process.platform !== 'win32') {
|
|||
this.paused = false;
|
||||
|
||||
if (this.buffer) {
|
||||
var buffer = this.buffer;
|
||||
const buffer = this.buffer;
|
||||
this.buffer = null;
|
||||
this._emitData(buffer);
|
||||
}
|
||||
|
@ -432,9 +432,9 @@ SerialPort.prototype.set = function(options, callback) {
|
|||
options = {};
|
||||
}
|
||||
|
||||
var settings = {};
|
||||
for (var i = SET_OPTIONS.length - 1; i >= 0; i--) {
|
||||
var flag = SET_OPTIONS[i];
|
||||
const settings = {};
|
||||
for (let i = SET_OPTIONS.length - 1; i >= 0; i--) {
|
||||
const flag = SET_OPTIONS[i];
|
||||
if (options[flag] !== undefined) {
|
||||
settings[flag] = options[flag];
|
||||
} else {
|
||||
|
|
113
views/example.js
113
views/example.js
|
@ -1,7 +1,8 @@
|
|||
const callbackStack = [];
|
||||
// eslint-disable-next-line no-undef
|
||||
const vscode = acquireVsCodeApi();
|
||||
|
||||
var example = new Vue({
|
||||
const example = new Vue({
|
||||
el: '#main',
|
||||
data: {
|
||||
version: '',
|
||||
|
@ -18,8 +19,8 @@ var example = new Vue({
|
|||
'https://raw.githubusercontent.com/VSChina/azureiotdevkit_tools/gallery/workbench-example-devkit-v2.json';
|
||||
this.boardId = query.board || '';
|
||||
httpRequest(url, function(data) {
|
||||
var examples = [];
|
||||
var aside = [];
|
||||
let examples = [];
|
||||
let aside = [];
|
||||
try {
|
||||
if (data) {
|
||||
data = JSON.parse(data);
|
||||
|
@ -36,7 +37,7 @@ var example = new Vue({
|
|||
document.getElementById('main').className = 'no-aside';
|
||||
}
|
||||
|
||||
for (var i = 0; i < examples.length; i++) {
|
||||
for (let i = 0; i < examples.length; i++) {
|
||||
examples[i].fullDescription = examples[i].description;
|
||||
if (examples[i].featured && !this.featuredExample) {
|
||||
this.featuredExample = examples.splice(i, 1)[0];
|
||||
|
@ -56,7 +57,7 @@ var example = new Vue({
|
|||
}
|
||||
|
||||
if (example.name) {
|
||||
let project_name = example.name.replace(/[^a-z0-9]/ig, '_').toLowerCase();
|
||||
const project_name = example.name.replace(/[^a-z0-9]/ig, '_').toLowerCase();
|
||||
return project_name;
|
||||
}
|
||||
|
||||
|
@ -79,7 +80,7 @@ function openLink(url, example) {
|
|||
}
|
||||
command('iotworkbench.openUri', url);
|
||||
if (example) {
|
||||
command('iotworkbench.sendTelemetry', {example});
|
||||
command('iotworkbench.sendTelemetry', { example });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +99,7 @@ function parseQuery(url) {
|
|||
return {};
|
||||
}
|
||||
const query = url.split('?')[1].split('&');
|
||||
let res = {};
|
||||
const res = {};
|
||||
query.forEach(q => {
|
||||
const item = q.split('=');
|
||||
res[item[0]] = item[1] ? decodeURIComponent(item[1]) : undefined;
|
||||
|
@ -108,14 +109,14 @@ function parseQuery(url) {
|
|||
}
|
||||
|
||||
function generateSection(obj, className) {
|
||||
let section = document.createElement('div');
|
||||
const section = document.createElement('div');
|
||||
section.className = 'section';
|
||||
if (className) {
|
||||
section.className += ' ' + className;
|
||||
}
|
||||
|
||||
if (obj.title) {
|
||||
let title = document.createElement('h1');
|
||||
const title = document.createElement('h1');
|
||||
title.innerText = obj.title;
|
||||
section.appendChild(title);
|
||||
}
|
||||
|
@ -123,12 +124,12 @@ function generateSection(obj, className) {
|
|||
}
|
||||
|
||||
function generateLinks(obj) {
|
||||
let section = generateSection(obj, 'quick-links');
|
||||
const section = generateSection(obj, 'quick-links');
|
||||
if (obj.items && obj.items.length) {
|
||||
let ulEl = document.createElement('ul');
|
||||
const ulEl = document.createElement('ul');
|
||||
ulEl.className = 'links';
|
||||
obj.items.forEach((link) => {
|
||||
let linkEl = document.createElement('li');
|
||||
const linkEl = document.createElement('li');
|
||||
linkEl.innerText = link.text;
|
||||
linkEl.addEventListener('click', () => {
|
||||
openLink(link.url);
|
||||
|
@ -141,14 +142,14 @@ function generateLinks(obj) {
|
|||
}
|
||||
|
||||
function generateTable(obj) {
|
||||
let section = generateSection(obj, 'info');
|
||||
const section = generateSection(obj, 'info');
|
||||
if (obj.rows && obj.rows.length) {
|
||||
let tableEl = document.createElement('table');
|
||||
const tableEl = document.createElement('table');
|
||||
obj.rows.forEach((row) => {
|
||||
if (row.length) {
|
||||
let trEl = document.createElement('tr');
|
||||
const trEl = document.createElement('tr');
|
||||
row.forEach((col) => {
|
||||
let tdEl = document.createElement('td');
|
||||
const tdEl = document.createElement('td');
|
||||
tdEl.innerText = col.text;
|
||||
if (col.url) {
|
||||
tdEl.className = 'link';
|
||||
|
@ -167,16 +168,16 @@ function generateTable(obj) {
|
|||
}
|
||||
|
||||
function generateText(obj) {
|
||||
let section = generateSection(obj);
|
||||
let pEl = document.createElement('p');
|
||||
const section = generateSection(obj);
|
||||
const pEl = document.createElement('p');
|
||||
pEl.innerText = obj.text;
|
||||
section.appendChild(pEl);
|
||||
return section;
|
||||
}
|
||||
|
||||
function generateImage(obj) {
|
||||
let section = generateSection(obj);
|
||||
let imgEl = document.createElement('img');
|
||||
const section = generateSection(obj);
|
||||
const imgEl = document.createElement('img');
|
||||
imgEl.src = obj.src;
|
||||
if (obj.url) {
|
||||
imgEl.className = 'link';
|
||||
|
@ -189,10 +190,10 @@ function generateImage(obj) {
|
|||
}
|
||||
|
||||
function generateBadge(obj) {
|
||||
let section = generateSection(obj, 'badge');
|
||||
const section = generateSection(obj, 'badge');
|
||||
if (obj.items && obj.items.length) {
|
||||
obj.items.forEach((item) => {
|
||||
let spanEl = document.createElement('span');
|
||||
const spanEl = document.createElement('span');
|
||||
spanEl.className = item.icon;
|
||||
spanEl.innerText = item.text;
|
||||
if (item.url) {
|
||||
|
@ -208,33 +209,33 @@ function generateBadge(obj) {
|
|||
}
|
||||
|
||||
function generateFeed(obj) {
|
||||
let section = generateSection(obj, 'blog');
|
||||
const section = generateSection(obj, 'blog');
|
||||
httpRequest(obj.url, function(data) {
|
||||
let parser = new DOMParser();
|
||||
let xmlDoc = parser.parseFromString(data,'text/xml');
|
||||
let items = xmlDoc.getElementsByTagName('item');
|
||||
let ulEl = document.createElement('ul');
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(data,'text/xml');
|
||||
const items = xmlDoc.getElementsByTagName('item');
|
||||
const ulEl = document.createElement('ul');
|
||||
ulEl.className = 'blog';
|
||||
for (let i = 0; i < Math.min(items.length, 3); i++) {
|
||||
let title = items[i].getElementsByTagName('title')[0].textContent;
|
||||
let link = items[i].getElementsByTagName('link')[0].textContent;
|
||||
let date = new Date(items[i].getElementsByTagName('pubDate')[0].textContent).toISOString().slice(0, 10);
|
||||
let description = items[i].getElementsByTagName('description')[0].textContent;
|
||||
const title = items[i].getElementsByTagName('title')[0].textContent;
|
||||
const link = items[i].getElementsByTagName('link')[0].textContent;
|
||||
const date = new Date(items[i].getElementsByTagName('pubDate')[0].textContent).toISOString().slice(0, 10);
|
||||
const description = items[i].getElementsByTagName('description')[0].textContent;
|
||||
|
||||
let liEl = document.createElement('li');
|
||||
let h2El = document.createElement('h2');
|
||||
const liEl = document.createElement('li');
|
||||
const h2El = document.createElement('h2');
|
||||
h2El.innerText = title;
|
||||
h2El.addEventListener('click', () => {
|
||||
openLink(link);
|
||||
});
|
||||
liEl.appendChild(h2El);
|
||||
|
||||
let divEl = document.createElement('div');
|
||||
const divEl = document.createElement('div');
|
||||
divEl.className = 'date';
|
||||
divEl.innerText = date;
|
||||
liEl.appendChild(divEl);
|
||||
|
||||
let pEl = document.createElement('p');
|
||||
const pEl = document.createElement('p');
|
||||
pEl.innerHTML = description;
|
||||
pEl.innerText = pEl.innerText;
|
||||
liEl.appendChild(pEl);
|
||||
|
@ -253,26 +254,26 @@ function generateAside(data) {
|
|||
data.forEach(item => {
|
||||
let section;
|
||||
switch(item.type) {
|
||||
case 'links':
|
||||
section = generateLinks(item);
|
||||
break;
|
||||
case 'table':
|
||||
section = generateTable(item);
|
||||
break;
|
||||
case 'text':
|
||||
section = generateText(item);
|
||||
break;
|
||||
case 'image':
|
||||
section = generateImage(item);
|
||||
break;
|
||||
case 'badge':
|
||||
section = generateBadge(item);
|
||||
break;
|
||||
case 'feed':
|
||||
section = generateFeed(item);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case 'links':
|
||||
section = generateLinks(item);
|
||||
break;
|
||||
case 'table':
|
||||
section = generateTable(item);
|
||||
break;
|
||||
case 'text':
|
||||
section = generateText(item);
|
||||
break;
|
||||
case 'image':
|
||||
section = generateImage(item);
|
||||
break;
|
||||
case 'badge':
|
||||
section = generateBadge(item);
|
||||
break;
|
||||
case 'feed':
|
||||
section = generateFeed(item);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (section) {
|
||||
|
@ -286,7 +287,7 @@ function command(cmd, callback) {
|
|||
if (!cmd) {
|
||||
return;
|
||||
}
|
||||
let args = Array.from(arguments);
|
||||
const args = Array.from(arguments);
|
||||
if (typeof args[args.length - 1] === 'function') {
|
||||
callback = args[args.length - 1];
|
||||
args.length = args.length - 1;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -42,21 +42,21 @@ function getDependeciesFromNpm(mod) {
|
|||
|
||||
/**@type {import('webpack').Configuration}*/
|
||||
const config = {
|
||||
target: 'node',
|
||||
target: 'node',
|
||||
|
||||
entry: getEntry(),
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'out/node_modules'),
|
||||
filename: '[name].js',
|
||||
libraryTarget: "commonjs2",
|
||||
devtoolModuleFilenameTemplate: "../[resource-path]",
|
||||
},
|
||||
externals: {
|
||||
vscode: "commonjs vscode"
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.json']
|
||||
}
|
||||
entry: getEntry(),
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'out/node_modules'),
|
||||
filename: '[name].js',
|
||||
libraryTarget: "commonjs2",
|
||||
devtoolModuleFilenameTemplate: "../[resource-path]",
|
||||
},
|
||||
externals: {
|
||||
vscode: "commonjs vscode"
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.json']
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = config;
|
Загрузка…
Ссылка в новой задаче