Initial build in new repo, switch to eslint

This commit is contained in:
anthonyvercolano 2022-10-31 08:21:25 -07:00
Родитель 58c225ead2
Коммит 57f950ee58
106 изменённых файлов: 24483 добавлений и 24 удалений

12
.eslintignore Normal file
Просмотреть файл

@ -0,0 +1,12 @@
# don't ever lint node_modules
node_modules
# don't lint build output (make sure it's set to your correct build folder name)
dist
# don't lint nyc coverage output
coverage
# don't lint tests
# test
# don't lint generated code
pl
# don't use rules on the config file
.eslintrc.js

148
.eslintrc.js Normal file
Просмотреть файл

@ -0,0 +1,148 @@
module.exports = {
"env": {
"commonjs": true,
"node": true,
"mocha": true,
"es6": true
},
"extends": [
"eslint:recommended",
"plugin:mocha/recommended"
],
"plugins": [
"@typescript-eslint",
"mocha",
"jsdoc"
],
"parserOptions": {
"ecmaVersion": 8
},
"overrides": [
{
"files": ["*.ts"],
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"extends": [
"plugin:@typescript-eslint/recommended",
// "plugin:@typescript-eslint/recommended-requiring-type-checking",
],
"rules": {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/member-ordering": ["error", {"default": ["abstract-method", "instance-method", "static-method"]}],
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
"@typescript-eslint/dot-notation": "error",
"@typescript-eslint/member-delimiter-style": [
"error",
{
"multiline": {
"delimiter": "semi",
"requireLast": true
},
"singleline": {
"delimiter": "semi",
"requireLast": false
}
}
],
"@typescript-eslint/typedef": [
"error",
{
"callSignature": true,
"parameter": true,
"propertyDeclaration": true,
"memberVariableDeclaration": true,
"arrowParameter": false
}
],
"@typescript-eslint/no-empty-function": "error",
"@typescript-eslint/no-unused-expressions": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/semi": [
"error",
"always"
],
"@typescript-eslint/type-annotation-spacing": "error",
// "@typescript-eslint/no-dupe-class-members": "off",
}
},
],
"rules": {
"mocha/no-setup-in-describe": "off",
"no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
"arrow-parens": [
"error",
"always"
],
"brace-style": [
"error",
"1tbs",
{ "allowSingleLine": true }
],
"comma-dangle": "off",
"eol-last": "error",
"eqeqeq": [
"error",
"smart"
],
"id-blacklist": [
"error",
"any",
"Number",
"number",
"String",
"string",
"Boolean",
"boolean",
"Undefined",
"undefined"
],
"id-match": "error",
"jsdoc/check-alignment": "error",
"jsdoc/newline-after-description": "error",
"linebreak-style": "off",
"max-len": "off",
"new-parens": "error",
"no-empty": "error",
"no-fallthrough": "error",
"no-invalid-this": "error",
"no-new-wrappers": "error",
"no-null/no-null": "off",
"no-redeclare": "error",
"no-trailing-spaces": "error",
"no-unsafe-finally": "error",
"no-var": "error",
"object-curly-spacing": [
"error",
"always"
],
"one-var": [
"error",
"never"
],
"require-jsdoc": "off",
"space-before-function-paren": [
"error",
{
"anonymous": "always",
"named": "never"
}
],
// "spaced-comment": [
// "error",
// "always",
// {
// "markers": [
// "/"
// ],
// "block": {
// "balanced": true
// }
// }
// ],
"use-isnan": "error"
}
}

227
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,227 @@
# Compiled object files
*.o
*.opp
# Compiled static libraries
*.a
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
[Bb]in/
[Oo]bj/
/build/release/Maven
!/build/release/
[Cc]make/
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
!packages/*/build/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Visual Studio user folder
.vs
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.VC.opendb
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
# NuGet Packages Directory
packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
*.jar
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml
# CPython & Wheels
*.pyc
*.pyd
*.whl
*.egg-info
# =========================
# Windows detritus
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac desktop service store files
.DS_Store
# Visual studio build artifacts
*.tlog
*.lastbuildstate
*.idb
*.exp
*.lib
*.dll
# Visual Studio Temporary files
*.VC.db
# hg directories should be ignored
**/hg/
# Node.js
**/.settings/
**/node_modules/
**/.idea/
**/coverage/
**/.nyc_output/
**/package-lock.json
# VS Code stuff
**/typings/**
**/.vscode/**
# C/C++ extension for VS Code
.browse.VC.db*
# Certificates
**/*.pem
# Typescript
**/*.map
# Typescript compiler output files
**/dist/
device/samples/typescript/dist
# Build artifacts
build/build_parallel/temp
# lerna
lerna-debug.log
# mocha
**/test-results.*.xml
# longhaul settings
**/_settings.json

100
README.md
Просмотреть файл

@ -1,33 +1,85 @@
# Project
# Microsoft Azure IoT service SDK for Node.js
> This repo has been populated by an initial template to help get you started. Please
> make sure to update the content to build a great experience for community-building.
The Azure IoT Service SDK for Node.js helps you build applications that interact with your devices and manage their identities in your IoT hub.
As the maintainer of this project, please make a few updates:
[![npm version](https://badge.fury.io/js/azure-iothub.svg)](https://badge.fury.io/js/azure-iothub)
- Improving this README.MD file to provide a great experience
- Updating SUPPORT.MD with content about this project's support experience
- Understanding the security reporting process in SECURITY.MD
- Remove this section from the README
## Prerequisites
You need to install the [Node.js][nodejs_lnk] JavaScript runtime environment to run the Azure IoT JavaScript client SDK on your platform. To check if Node.js supports your platform (OS), verify that an install package is available on the [Node.js download page][nodejs_dwld_lnk].
## Contributing
[npm][npm_lnk] is a command-line package manager that is installed with Node.js is installed, and will be used to install Azure IoT node.js client side SDK.
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
## Installation
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
`npm install azure-iothub` to get the latest version.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Features
## Trademarks
* Create/remove/update/list device identities in your IoT hub
* Send messages to your devices and get feedback when they're delivered
* Work with the Azure IoT Hub Device Twins
* Invoke Cloud to Device Direct Methods on a device
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
trademarks or logos is subject to and must follow
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Any use of third-party trademarks or logos are subject to those third-party's policies.
## How to use the Azure IoT service SDK for Node.js
Once you have installed the package as indicated above, you can start using the features of the Service SDK in your code. Below is a code snippet showing how to add a new device in the Azure IoT Hub device registry:
Note that for this sample to work, you will need to [setup your IoT hub][lnk-setup-iot-hub] and retrieve credentials for the service app. Utilize the '[IoT Connection String]', in quotes, on the command line invoking the sample.
```js
var iothub = require('azure-iothub');
var connectionString = '[IoT Connection String]';
var registry = iothub.Registry.fromConnectionString(connectionString);
// Create a new device
var device = {
deviceId: 'sample-device-' + Date.now()
};
registry.create(device, function(err, deviceInfo, res) {
if (err) console.log(op + ' error: ' + err.toString());
if (res) console.log(op + ' status: ' + res.statusCode + ' ' + res.statusMessage);
if (deviceInfo) console.log(op + ' device info: ' + JSON.stringify(deviceInfo));
});
```
Check out the [samples][samples] for details on the various features of the Service SDK
## Read more
* [Azure IoT Hub dev center][iot-dev-center]
* [Azure IoT Hub documentation][iot-hub-documentation]
* [Node.js API reference documentation][node-api-reference]
## Directory structure
Service SDK subfolders:
### /devdoc
Development requirements documentation
### /src
Code for the library
### /samples
Set of simple samples showing how to use the features of the Service SDK
### /test
Test files
[nodejs_lnk]: https://nodejs.org/
[nodejs_dwld_lnk]: https://nodejs.org/en/download/
[npm_lnk]:https://docs.npmjs.com/getting-started/what-is-npm
[samples]: ./samples/
[lnk-setup-iot-hub]: https://aka.ms/howtocreateazureiothub
[node-api-reference]: https://docs.microsoft.com/en-us/javascript/api/azure-iothub/
[iot-dev-center]: http://azure.com/iotdev
[iot-hub-documentation]: https://docs.microsoft.com/en-us/azure/iot-hub/

18
iothub.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,18 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
export { Client } from './dist/client';
export { ConnectionString } from './dist/connection_string';
export { versionQueryString } from './dist/version';
export { Registry } from './dist/registry';
export import SharedAccessSignature = require('./dist/shared_access_signature');
export { Amqp } from './dist/amqp';
export { AmqpWs } from './dist/amqp_ws';
export { DeviceMethodParams } from './dist/interfaces';
export { JobClient } from './dist/job_client';
export * from './dist/configuration';
export { Device } from './dist/device';
export { Module } from './dist/module';
export { Twin } from './dist/twin';
export { IoTHubTokenCredentials } from './dist/auth/iothub_token_credentials';
export { DigitalTwinClient } from './dist/cl/digital_twin_client';

37
iothub.js Normal file
Просмотреть файл

@ -0,0 +1,37 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
/**
* The Azure IoT Service SDK for Node.js allows applications to interact with an Azure IoT hub with:
* A messaging {@link azure-iothub.Client} using either AMQP or AMQP over Websockets that enables:
* - sending cloud-to-device messages (also known as commands) to devices
* - execute direct methods on devices
* - listening for feedback when cloud-to-device messages are delivered
* - listening for file upload notifications from devices
*
* It also supports Device identity registry operations with the {@link azure-iothub.Registry} object:
* - creating, removing, updating, and listing device identities registered with an IoT hub
* - get and update and query device twins
*
* Finally, the `{@link azure-iothub.JobClient} object allows to schedule long running tasks that:
* - execute direct methods on one or more devices at a specific time
* - update one or more device twins at a specific time
*
* @module azure-iothub
*/
module.exports = {
Client: require('./dist/client.js').Client,
version: require('./dist/version.js'),
ConnectionString: require('./dist/connection_string.js'),
Registry: require('./dist/registry.js').Registry,
SharedAccessSignature: require('./dist/shared_access_signature.js'),
Amqp: require('./dist/amqp.js').Amqp,
AmqpWs: require('./dist/amqp_ws.js').AmqpWs,
JobClient: require('./dist/job_client.js').JobClient,
Device: require('./dist/device.js').Device,
Twin: require('./dist/twin.js').Twin,
IoTHubTokenCredentials: require('./dist/auth/iothub_token_credentials').IoTHubTokenCredentials,
DigitalTwinClient: require('./dist/cl/digital_twin_client').DigitalTwinClient
};

130
package.json Normal file
Просмотреть файл

@ -0,0 +1,130 @@
{
"name": "azure-iothub",
"version": "1.16.0",
"description": "Azure IoT SDK - IoT Hub",
"author": "Microsoft Corporation",
"license": "MIT",
"main": "iothub.js",
"typings": "iothub.d.ts",
"dependencies": {
"@azure/core-http": "^1.2.3",
"@azure/identity": "^2.0.0",
"@azure/ms-rest-js": "^2.0.5",
"async": "^3.2.3",
"azure-iot-amqp-base": "2.5.0",
"azure-iot-common": "1.13.0",
"azure-iot-http-base": "1.12.0",
"debug": "^4.3.1",
"es5-ext": "0.10.53",
"lodash": "^4.17.21",
"machina": "^4.0.2",
"rhea": "^1.0.15",
"tslib": "^1.9.3"
},
"devDependencies": {
"@types/debug": "^4.1.5",
"@types/node": "^16.10.2",
"autorest": "^2.0.4413",
"@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/eslint-plugin-tslint": "^5.41.0",
"@typescript-eslint/parser": "^5.41.0",
"eslint": "^8.26.0",
"eslint-plugin-jsdoc": "^39.3.25",
"eslint-plugin-mocha": "^10.1.0",
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-promise": "^6.1.1",
"chai": "^4.3.3",
"jshint": "^2.13.4",
"mocha": "^9.2.1",
"nyc": "^15.0.0",
"sinon": "^11.1.2",
"source-map-support": "^0.5.16",
"ts-node": "^8.6.2",
"typescript": "^4.4.4",
"uuid": "^8.3.2"
},
"files": [
"dist/**/*.js",
"dist/**/*.js.map",
"dist/**/*.d.ts",
"dist/**/*.d.ts.map",
"src/**/*.ts",
"tsconfig.json",
"iothub.js",
"iothub.d.ts"
],
"scripts": {
"hublint": "eslint src --ext .ts -f visualstudio",
"diglint": "eslint src/cl --ext .ts -f visualstudio",
"tstlint": "eslint test --ext .js -f visualstudio",
"lint": "npm run hublint && npm run diglint && npm run tstlint",
"build": "tsc",
"job-client": "node node_modules/mocha/bin/_mocha ./test/_job_client_test.js",
"registry": "node node_modules/mocha/bin/_mocha ./test/_registry_test.js",
"amqp-service-errors": "node node_modules/mocha/bin/_mocha ./test/_amqp_service_errors_test.js",
"amqp": "node node_modules/mocha/bin/_mocha ./test/_amqp_test.js",
"amqp-ws": "node node_modules/mocha/bin/_mocha --package ./nul -r ./test/_amqp_ws_test.js",
"client": "node node_modules/mocha/bin/_mocha ./test/_client_test.js",
"connection-string": "node node_modules/mocha/bin/_mocha ./test/_connection_string_test.js",
"device-method": "node node_modules/mocha/bin/_mocha ./test/_device_method_test.js",
"device": "node node_modules/mocha/bin/_mocha ./test/_device_test.js",
"digital-twin-client": "node node_modules/mocha/bin/_mocha ./test/_digital_twin_client_test.js",
"iothub-registry-manager": "node node_modules/mocha/bin/_mocha ./test/_iothub_registry_manager_test.js",
"iothub-token-credentials": "node node_modules/mocha/bin/_mocha ./test/_iothub_token_credentials_test.js",
"query": "node node_modules/mocha/bin/_mocha ./test/_query_test.js",
"service-receiver": "node node_modules/mocha/bin/_mocha ./test/_service_receiver_test.js",
"shared-access-signature": "node node_modules/mocha/bin/_mocha ./test/_shared_access_signature_test.js",
"amqp-simulated": "node node_modules/mocha/bin/_mocha ./test/amqp_simulated.js",
"twin": "node node_modules/mocha/bin/_mocha ./test/_twin_test.js",
"unittest-min": "tsc && nyc --reporter lcov ./node_modules/mocha/bin/_mocha --reporter dot --parallel test/**/_*_test.js",
"alltest-min": "tsc && nyc --reporter lcov ./node_modules/mocha/bin/_mocha --reporter dot --parallel test/_*_test*.js",
"unittest": "tsc && nyc --reporter lcov --reporter text ./node_modules/mocha/bin/_mocha --parallel test/**/_*_test.js",
"alltest": "tsc && nyc --reporter lcov --reporter text ./node_modules/mocha/bin/_mocha --parallel test/_*_test*.js",
"ci": "npm -s run lint && npm -s run build && npm -s run alltest-min",
"test": "npm -s run lint && npm -s run build && npm -s run alltest",
"generate-pl": "autorest --typescript --add-credentials --model-enum-as-union --license-header=MICROSOFT_MIT_NO_VERSION --source-code-folder-path=./src/pl --output-folder=./src/pl --input-file=src/service.json"
},
"nyc": {
"exclude": [
"coverage/**",
"**/*.d.ts",
"test{,s}/**",
"test{,-*}.{js,cjs,mjs,ts}",
"**/*{.,-}test.{js,cjs,mjs,ts}",
"**/__tests__/**",
"**/{ava,nyc}.config.{js,cjs,mjs}",
"**/jest.config.{js,cjs,mjs,ts}",
"**/{karma,rollup,webpack}.config.js",
"**/{babel.config,.eslintrc,.mocharc}.{js,cjs}"
],
"extension": [
".ts",
".tsx"
],
"check-coverage": false,
"lines": 94,
"functions": 83,
"branches": 95,
"statements": 95
},
"mocha": {
"require": [
"ts-node/register",
"source-map-support/register"
],
"full-trace": true,
"bail": true
},
"engines": {
"node": ">= 14.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Azure/azure-iot-hub-node.git"
},
"bugs": {
"url": "https://github.com/Azure/azure-iot-hub-node/issues"
},
"homepage": "https://github.com/Azure/azure-iot-hub-node#readme"
}

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

@ -0,0 +1,136 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var registry = iothub.Registry.fromConnectionString(connectionString);
var sampleConfigId = 'chiller4000x';
var sampleConfig = {
id: sampleConfigId,
content: {
deviceContent: {
'properties.desired.chiller-water': {
temperature: 66,
pressure: 28
}
}
},
metrics: {
queries: {
waterSettingsPending: 'SELECT deviceId FROM devices WHERE properties.reported.chillerWaterSettings.status=\'pending\''
}
},
targetCondition: 'properties.reported.chillerProperties.model=\'4000x\'',
priority: 20
};
var printJson = function(obj) {
console.log(JSON.stringify(obj, null, ' '));
};
var getAllConfigurations = function(done) {
console.log();
console.log('Querying IoT Hub for configurations');
registry.getConfigurations(function(err, configurations) {
if (err) {
console.log('getConfigurations failed: ' + err);
done();
} else {
console.log(configurations.length.toString() + ' configurations found');
configurations.forEach(function(config) {
console.log('contents of ' + config.id + ':');
printJson(config);
console.log();
});
done();
}
});
};
var createConfiguration = function(done) {
console.log();
console.log('adding new configuration with id ' + sampleConfig.id + ' and priority ' + sampleConfig.priority);
registry.addConfiguration(sampleConfig, function(err) {
if (err) {
console.log('add configuration failed: ' + err);
done();
} else {
console.log('add configuration succeeded');
done();
}
});
};
var monitorConfiguration = function(done) {
console.log();
console.log('getting details for config ' + sampleConfigId);
registry.getConfiguration(sampleConfigId, function(err, config) {
if (err) {
console.log('getConfigurationById failed: ' + err);
done();
} else {
printJson(config);
done();
}
});
};
var updateConfiguration = function(done) {
console.log();
console.log('updating configuration ' + sampleConfigId + ' to add new query' );
registry.getConfiguration(sampleConfigId, function(err, configFromService) {
if (err) {
console.log('getConfiguration failed: ' + err);
done();
} else {
configFromService.metrics.queries.overheat = 'SELECT deviceId FROM devices WHERE properties.reported.chillerWaterSettings.temperature > 75';
registry.updateConfiguration(configFromService, function(err) {
if (err) {
console.log('updateConfiguration failed: ' + err);
done();
} else {
console.log('updateConfiguration succeeded');
done();
}
});
}
});
};
var removeConfiguration = function(done) {
console.log();
console.log('removing configuration with id ' + sampleConfigId);
registry.removeConfiguration(sampleConfigId, function(err) {
if (err) {
console.log('removeConfiguration failed: ' + err);
done();
} else {
console.log('removeConfiguration succeeded');
done();
}
});
};
getAllConfigurations(function() {
createConfiguration(function() {
monitorConfiguration(function() {
updateConfiguration(function() {
monitorConfiguration(function() {
removeConfiguration(function() {
});
});
});
});
});
});

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

@ -0,0 +1,33 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var registry = iothub.Registry.fromConnectionString(connectionString);
var device = {
deviceId: process.env.IOTHUB_DEVICE_ID,
status: 'enabled',
authentication: {
x509Thumbprint: {
primaryThumbprint: process.env.IOTHUB_PRIMARY_THUMBPRINT,
secondaryThumbprint: process.env.IOTHUB_SECONDARY_THUMBPRINT
}
}
};
registry.create(device, function (err) {
if(err) {
console.error('Could not create device: ' + err.message);
} else {
registry.get(device.deviceId, function(err, deviceInfo) {
if(err) {
console.error('Could not get device: ' + err.message);
} else {
console.log(JSON.stringify(deviceInfo));
}
});
}
});

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

@ -0,0 +1,34 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const { DefaultAzureCredential } = require("@azure/identity");
var iothub = require('azure-iothub');
var hostName = process.env.AZURE_AAD_HOST;
// DefaultAzureCredential expects the following three environment variables:
// - AZURE_TENANT_ID: The tenant ID in Azure Active Directory
// - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant
// - AZURE_CLIENT_SECRET: The client secret for the registered application
const credential = new DefaultAzureCredential();
var registry = iothub.Registry.fromTokenCredential(hostName, credential);
var device = {
deviceId: process.env.IOTHUB_DEVICE_ID,
status: 'enabled'
};
registry.create(device, function (err) {
if(err) {
console.error('Could not create device: ' + err.message);
} else {
registry.get(device.deviceId, function(err, deviceInfo) {
if(err) {
console.error('Could not get device: ' + err.message);
} else {
console.log(JSON.stringify(deviceInfo));
}
});
}
});

25
samples/device_method.js Normal file
Просмотреть файл

@ -0,0 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var Client = require('azure-iothub').Client;
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var targetDevice = process.env.IOTHUB_DEVICE_ID;
var methodParams = {
methodName: process.env.IOTHUB_METHOD_NAME,
payload: process.env.IOTHUB_METHOD_PAYLOAD,
responseTimeoutInSeconds: 15 // set response timeout as 15 seconds
};
var client = Client.fromConnectionString(connectionString);
client.invokeDeviceMethod(targetDevice, methodParams, function (err, result) {
if (err) {
console.error('Failed to invoke method \'' + methodParams.methodName + '\': ' + err.message);
} else {
console.log(methodParams.methodName + ' on ' + targetDevice + ':');
console.log(JSON.stringify(result, null, 2));
}
});

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

@ -0,0 +1,61 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var Registry = require('azure-iothub').Registry;
var Client = require('azure-iothub').Client;
var async = require('async');
// receive the target device ID as a command line parameter
if (process.argv.length < 3) {
console.error('Usage: node dmpatterns_fwupdate_service.js <<targetDeviceId>>');
process.exit(1);
}
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var deviceToUpdate = process.argv[2];
var registry = Registry.fromConnectionString(connectionString);
var client = Client.fromConnectionString(connectionString);
// Service entry point: Initiate the firmware update process on the device using a device method
async.waterfall([
invokeFirmwareUpdate,
displayFirmwareUpdateStatus
],
function(err) {
if (err) {
console.error(err);
} else {
console.log('Firmware update complete');
}
});
// Initiate the firmware update through a method
function invokeFirmwareUpdate(callback) {
client.invokeDeviceMethod(deviceToUpdate,
{
methodName: "firmwareUpdate",
payload: {
fwPackageUri: 'https://secureurl'
},
timeoutInSeconds: 30
}, function (err, result) {
console.log(JSON.stringify(result, null, 2));
callback(err);
}
);
}
// Get the twin and output the firmwareUpdate status from reported properties
function displayFirmwareUpdateStatus(callback) {
registry.getTwin(deviceToUpdate, function(err, twin){
if (err) {
callback(err);
} else {
// Output the value of twin reported properties, which includes the firmwareUpdate details
console.log(twin.properties.reported);
callback(null);
}
});
}

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

@ -0,0 +1,60 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var Registry = require('azure-iothub').Registry;
var Client = require('azure-iothub').Client;
var async = require('async');
// receive the IoT Hub connection string as a command line parameter
if(process.argv.length < 4) {
console.error('Usage: node dmpatterns_reboot_service.js <<IoT Hub Connection String>> <<targetDeviceId>>');
process.exit(1);
}
var connectionString = process.argv[2];
var registry = Registry.fromConnectionString(connectionString);
var client = Client.fromConnectionString(connectionString);
var deviceToReboot = process.argv[3];
// Initiate the reboot process on the device using a device method
async.waterfall([
invokeReboot,
displayRebootStatus
],
function(err) {
if (err){
console.error(err);
} else {
console.log('Reboot complete');
}
});
// Initiate the reboot through a method
function invokeReboot(callback) {
client.invokeDeviceMethod(deviceToReboot,
{
methodName: "reboot",
payload: null,
timeoutInSeconds: 30
}, function (err, result) {
console.log(JSON.stringify(result, null, 2));
callback(err);
}
);
}
// Get the twin and output the reboot status from reported properties
function displayRebootStatus(callback) {
registry.getTwin(deviceToReboot, function(err, twin){
if (err) {
callback(err);
}
else {
// Output the value of twin reported properties, which includes the reboot details
console.log(twin.properties.reported);
callback(null);
}
});
}

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

@ -0,0 +1,152 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var registry = iothub.Registry.fromConnectionString(connectionString);
var sampleDeploymentId = 'fake-deployment';
var sampleDeployment = {
'id': sampleDeploymentId,
'content': {
'modulesContent': {
'$edgeAgent': {
'properties.desired': {
'schemaVersion': '1.0',
'runtime': {
'type': 'docker',
'settings': {
'minDockerVersion': 'v1.25',
'loggingOptions': ''
}
},
'systemModules': {
'edgeAgent': {
'type': 'docker',
'settings': {
'image': 'microsoft/azureiotedge-agent:1.0-preview',
'createOptions': '{}'
}
},
'edgeHub': {
'type': 'docker',
'status': 'running',
'restartPolicy': 'always',
'settings': {
'image': 'microsoft/azureiotedge-hub:1.0-preview',
'createOptions': '{}'
}
}
},
'modules': {}
}
},
'$edgeHub': {
'properties.desired': {
'schemaVersion': '1.0',
'routes': {
'route': 'FROM /* INTO $upstream'
},
'storeAndForwardConfiguration': {
'timeToLiveSecs': 7200
}
}
}
}
},
'schemaVersion': '1.0',
'targetCondition': 'tags.environment=\'test\'',
'priority': 10,
'labels': {
'Version': '3.0.1'
},
};
var printJson = function(obj) {
console.log(JSON.stringify(obj, null, ' '));
};
var createEdgeDeployment = function(done) {
console.log();
console.log('adding new deployment with id ' + sampleDeployment.id);
registry.addConfiguration(sampleDeployment, function(err) {
if (err) {
console.log('add configuration failed: ' + err);
done();
} else {
console.log('add configuration succeeded');
done();
}
});
};
var monitorEdgeDeployment = function(done) {
console.log();
console.log('getting details for deployment ' + sampleDeploymentId);
registry.getConfiguration(sampleDeploymentId, function(err, config) {
if (err) {
console.log('getConfigurationById failed: ' + err);
done();
} else {
printJson(config);
done();
}
});
};
var updateEdgeDeployment = function(done) {
console.log();
console.log('updating deployment ' + sampleDeploymentId + ' to add new query' );
registry.getConfiguration(sampleDeploymentId, function(err, configFromService) {
if (err) {
console.log('getConfiguration failed: ' + err);
done();
} else {
configFromService.metrics.queries.notAppliedCount = 'select deviceId from devices.modules where moduleId = \'$edgeAgent\' and configurations.[[fake-deployment]].status != \'Applied\'';
registry.updateConfiguration(configFromService, function(err) {
if (err) {
console.log('updateConfiguration failed: ' + err);
done();
} else {
console.log('updateConfiguration succeeded');
done();
}
});
}
});
};
var removeEdgeDeployment = function(done) {
console.log();
console.log('removing configuration with id ' + sampleDeploymentId);
registry.removeConfiguration(sampleDeploymentId, function(err) {
if (err) {
console.log('removeConfiguration failed: ' + err);
done();
} else {
console.log('removeConfiguration succeeded');
done();
}
});
};
createEdgeDeployment(function() {
monitorEdgeDeployment(function() {
updateEdgeDeployment(function() {
monitorEdgeDeployment(function() {
removeEdgeDeployment(function() {
});
});
});
});
});

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

@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const IoTHubTokenCredentials = require('azure-iothub').IoTHubTokenCredentials;
const DigitalTwinClient = require('azure-iothub').DigitalTwinClient;
const { inspect } = require('util');
// Simple example of how to:
// - create a Digital Twin Client using the DigitalTwinClient constructor
// - get the Digital Twin
//
// Preconditions:
// - Environment variables have to be set
// - Twin enabled device must exist on the ADT hub
async function main() {
const deviceId = process.env.IOTHUB_DEVICE_ID;
const connectionString = process.env.IOTHUB_CONNECTION_STRING;
// Create digital twin client
const credentials = new IoTHubTokenCredentials(connectionString);
const digitalTwinClient = new DigitalTwinClient(credentials);
// Get digital twin and retrieve the modelId from it
const digitalTwin = await digitalTwinClient.getDigitalTwin(deviceId);
console.log(inspect(digitalTwin));
console.log('Model Id: ' + inspect(digitalTwin.$metadata.$model));
}
main().catch((err) => {
console.log('error code: ', err.code);
console.log('error message: ', err.message);
console.log('error stack: ', err.stack);
});

43
samples/invoke_command.js Normal file
Просмотреть файл

@ -0,0 +1,43 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const IoTHubTokenCredentials = require('azure-iothub').IoTHubTokenCredentials;
const DigitalTwinClient = require('azure-iothub').DigitalTwinClient;
const { inspect } = require('util');
// Simple example of how to:
// - create a Digital Twin Client using the DigitalTwinClient constructor
// - invoke a root level command on a Digital Twin enabled device
//
// Preconditions:
// - Environment variables have to be set
// - Twin enabled device must exist on the ADT hub
async function main() {
const deviceId = process.env.IOTHUB_DEVICE_ID;
const connectionString = process.env.IOTHUB_CONNECTION_STRING;
const commandName = process.env.IOTHUB_COMMAND_NAME; // for the thermostat you can try getMaxMinReport
const commandPayload = process.env.IOTHUB_COMMAND_PAYLOAD; // it really doesn't matter, any string will do.
// Create service client
const credentials = new IoTHubTokenCredentials(connectionString);
const digitalTwinClient = new DigitalTwinClient(credentials);
// Invoke a command
const options = {
connectTimeoutInSeconds: 30,
responseTimeoutInSeconds: 40 // The responseTimeoutInSeconds must be within [5; 300]
};
const commandResponse = await digitalTwinClient.invokeCommand(deviceId, commandName, commandPayload, options);
// Print result of the command
console.log(inspect(commandResponse));
}
main().catch((err) => {
console.log('error code: ', err.code);
console.log('error message: ', err.message);
console.log('error stack: ', err.stack);
});

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

@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const IoTHubTokenCredentials = require('azure-iothub').IoTHubTokenCredentials;
const DigitalTwinClient = require('azure-iothub').DigitalTwinClient;
const { inspect } = require('util');
// Simple example of how to:
// - create a Digital Twin Client using the DigitalTwinClient constructor
// - invoke a command on a Digital Twin enabled device's component
//
// Preconditions:
// - Environment variables have to be set
// - Twin enabled device must exist on the ADT hub
async function main() {
const deviceId = process.env.IOTHUB_DEVICE_ID;
const connectionString = process.env.IOTHUB_CONNECTION_STRING;
const componentName = process.env.IOTHUB_COMPONENT_NAME; // for the TemperatureController, try thermostat1
const commandName = process.env.IOTHUB_COMMAND_NAME; // for the thermostat you can try getMaxMinReport
const commandArgument = process.env.IOTHUB_COMMAND_PAYLOAD; // it really doesn't matter, any string will do.
// Create service client
const credentials = new IoTHubTokenCredentials(connectionString);
const digitalTwinClient = new DigitalTwinClient(credentials);
// Invoke a command
const options = {
connectTimeoutInSeconds: 30,
responseTimeoutInSeconds: 40 // The responseTimeoutInSeconds must be within [5; 300]
};
const commandResponse = await digitalTwinClient.invokeComponentCommand(deviceId, componentName, commandName, commandArgument, options);
// Print result of the command
console.log(inspect(commandResponse));
}
main().catch((err) => {
console.log('error code: ', err.code);
console.log('error message: ', err.message);
console.log('error stack: ', err.stack);
});

26
samples/job_query.js Normal file
Просмотреть файл

@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var JobClient = require('azure-iothub').JobClient;
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var jobClient = JobClient.fromConnectionString(connectionString);
var query = jobClient.createQuery();
var onResults = function(err, results) {
if (err) {
console.error('Failed to fetch the results: ' + err.message);
} else {
// Do something with the results
results.forEach(function(job) {
console.log(JSON.stringify(job, null, 2));
});
if (query.hasMoreResults) {
query.next(onResults);
}
}
};
query.next(onResults);

27
samples/module_method.js Normal file
Просмотреть файл

@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var Client = require('azure-iothub').Client;
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var deviceId = process.env.IOTHUB_DEVICE_ID;
var moduleId = process.env.IOTHUB_MODULE_ID;
var methodParams = {
methodName: process.env.IOTHUB_METHOD_NAME,
payload: process.env.IOTHUB_METHOD_PAYLOAD,
responseTimeoutInSeconds: 15 // set response timeout as 15 seconds
};
var client = Client.fromConnectionString(connectionString);
client.invokeDeviceMethod(deviceId, moduleId, methodParams, function (err, result) {
if (err) {
console.error('Failed to invoke method \'' + methodParams.methodName + '\': ' + err.message);
} else {
console.log(methodParams.methodName + ' on ' + deviceId + ':');
console.log(JSON.stringify(result, null, 2));
}
});

98
samples/module_sample.js Normal file
Просмотреть файл

@ -0,0 +1,98 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var deviceId = process.env.IOTHUB_DEVICE_ID;
var moduleId = process.env.IOTHUB_MODULE_ID;
var registry = iothub.Registry.fromConnectionString(connectionString);
var getModulesOnDevice = function(done) {
console.log();
console.log('getting all modules from device ' + deviceId);
registry.getModulesOnDevice(deviceId, function(err, modules) {
if (err) {
console.log('getModulesOnDevice failed ' + err);
done();
} else {
console.log(modules.length.toString() + ' modules found');
modules.forEach(function(module) {
console.log(module.moduleId);
});
done();
}
});
};
var addNewModule = function(done) {
console.log();
console.log('adding new module with moduleId=' + moduleId);
registry.addModule({deviceId: deviceId, moduleId: moduleId}, function(err) {
if (err) {
console.log('addModule failed ' + err);
done();
} else {
console.log('addModule succeeded');
done();
}
});
};
var updateModule = function(done) {
console.log();
console.log('updating module with moduleId=' + moduleId);
registry.getModule(deviceId, moduleId, function(err, module) {
if (err) {
console.log('getModule failed ' + err);
done();
} else {
console.log('getModule succeeded');
var oldPrimary = module.authentication.symmetricKey.primaryKey;
module.authentication.symmetricKey.primaryKey = module.authentication.symmetricKey.secondaryKey;
module.authentication.symmetricKey.secondaryKey = oldPrimary;
console.log('using updateModule to set primary key to ' + module.authentication.symmetricKey.primaryKey);
registry.updateModule(module, function(err) {
if (err) {
console.log('updateModule failed ' + err);
done();
} else {
console.log('updateModule succeeded');
done();
}
});
}
});
};
var removeModule = function(done) {
console.log();
console.log('removing module with moduleId=' + moduleId);
registry.removeModule(deviceId, moduleId, function(err) {
if (err) {
console.log('removeModule failed ' + err);
done();
} else {
console.log('removeModule succeeded');
done();
}
});
};
getModulesOnDevice(function() {
addNewModule(function() {
updateModule(function() {
removeModule(function() {
});
});
});
});

47
samples/module_twin.js Normal file
Просмотреть файл

@ -0,0 +1,47 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
"use strict";
var Registry = require('azure-iothub').Registry;
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var deviceId = process.env.IOTHUB_DEVICE_ID;
var moduleId = process.env.IOTHUB_MODULE_ID;
var registry = Registry.fromConnectionString(connectionString);
registry.getModuleTwin(deviceId, moduleId, function(err, twin) {
if (err) {
console.error(err.message);
} else {
console.log(JSON.stringify(twin, null, 2));
var twinPatch = {
tags: {
city: "Redmond"
},
properties: {
desired: {
telemetryInterval: 1000
}
}
};
// method 1: using the update method directly on the twin
twin.update(twinPatch, function(err, twin) {
if (err) {
console.error(err.message);
} else {
console.log(JSON.stringify(twin, null, 2));
// method 2: using the updateTwin method on the Registry object
registry.updateModuleTwin(twin.deviceId, twin.moduleId, { properties: { desired: { telemetryInterval: 2000 }}}, twin.etag, function(err, twin) {
if (err) {
console.error(err.message);
} else {
console.log(JSON.stringify(twin, null, 2));
}
});
}
});
}
});

22
samples/package.json Normal file
Просмотреть файл

@ -0,0 +1,22 @@
{
"name": "registry_sample",
"version": "0.0.1",
"private": true,
"main": "registry_sample.js",
"author": "Microsoft Corp.",
"license": "MIT",
"scripts": {
"npmlockrefresh": "npm i --package-lock-only",
"lint": "jshint --show-non-errors .",
"ci": "npm run lint"
},
"dependencies": {
"async": "^3.2.3",
"es5-ext": "0.10.53",
"azure-iothub": "1.15.0",
"azure-storage": "^2.10.4"
},
"devDependencies": {
"jshint": "^2.13.4"
}
}

96
samples/readme.md Normal file
Просмотреть файл

@ -0,0 +1,96 @@
# Samples for the Azure IoT service SDK for Node.js
This folder contains simple samples showing how to use the various features of the Microsoft Azure IoT Hub service from a device running C code.
## List of samples
* Registry
* **registry_sample.js**: Manage the device ID registry of IoT Hub.
* **registry_bulk_sample.js**: Create a set of device IDs in the device ID registry of IoT Hub in bulk.
* **create_device_with_cert.js**: Create a new device ID using an X-509 certificate.
* Messaging
* **send_c2d_message.js** : Send C2D messages to a device through IoT Hub.
* Device services samples (Device Twins, Methods, and Device Management) (See [device management patterns][dm-patterns] for instructions on running the device management patterns samples):
* **twin.js**: Interact with the Device Twins from a back-end app.
* **twin_query.js**: Interact with the Device Twins using queries from a back-end app.
* **device_method.js**: Invoke a C2D Direct Method on a device through IoT Hub.
* **dmpatterns_reboot_service.js**: Initiate a C2D method to reboot a device and view progress through the twin reported properties.
* **dmpatterns_fwupdate_service.js**: Implement the service side of the firmware update DM pattern.
* **job_query.js**: Use the jobs query feature of the service SDK.
* **schedule_job.js**: Schedule device management jobs.
* Uploading blob to Azure:
* **receive_file_notifications.js**: Track the progress of the file "upload to blob" by devices.
* Plug and Play
* **invoke_command.js** invokes a command on a device interface instance.
* **invoke_component_command.js** invokes a command that is defined by a specific component.
* **get_digital_twin.js** gets the digital twin for a specific device.
* **update_digital_twin.js** creates a patch to updates multiple writable properties on a digital twin, potentially on multiple interface instances.
## How to run the samples
In order to run the device samples you will first need the following prerequisites:
* Node.js 10 or above on your target device. (Check out [Nodejs.org](https://nodejs.org/) for more info)
* [Create an Azure IoT Hub instance][lnk-setup-iot-hub]
Get the following files from the current folder:
* **package.json**
* **__sample_file.js__** (where **__sample_file.js__** is one of the files listed above and available in this folder)
Place the files in the folder of your choice on the target machine/device then go through the following steps:
* Open the file **__sample_file.js__** in a text editor.
* Set the following environment variable:
```shell
set IOTHUB_CONNECTION_STRING=<your IoT Hub connection string>
```
*use `export` instead of `set` if you're running MacOS or Linux.*
* Depending on the samples you can also set the following environment variables:
```shell
set IOTHUB_DEVICE_ID=<device id>
set IOTHUB_PRIMARY_THUMBPRINT=<primary thumbprint>
set IOTHUB_SECONDARY_THUMBPRINT=<secondary thumbprint>
set IOTHUB_MODULE_ID=<module id>
set IOTHUB_METHOD_NAME=<method name>
set IOTHUB_METHOD_PAYLOAD=<method payload>
set STORAGE_CONNECTION_STRING=<storage connection string>
set IOTHUB_DEVICE_ID2=<second device id>
```
* additionally some Plug and Play samples take the following environment variables:
```shell
set IOTHUB_COMPONENT_NAME=<name of the component>
set IOTHUB_COMMAND_NAME=<command to execute>
set IOTHUB_COMMAND_PAYLOAD=<json payload for the above command>
```
* From a shell or Node.js command prompt, navigate to the folder where you placed the sample files. Run the sample application using the following commands:
```
npm install
node sample_file.js
```
To run the Device Management samples, follow our [DM patterns documentation][dm-patterns].
## Read More
For more information on how to use this library refer to the documents below:
- [Prepare your node.js development environment](../../../doc/node-devbox-setup.md)
- [Setup IoT Hub][lnk-setup-iot-hub]
- [Node API reference][node-api-reference]
- [Debugging with Visual Studio Code][debug-with-vscode]
- [Use the Azure IoT Explorer tool][iothub-explorer]
[lnk-setup-iot-hub]: https://aka.ms/howtocreateazureiothub
[remote-monitoring-pcs]: https://docs.microsoft.com/en-us/azure/iot-suite/iot-suite-remote-monitoring-sample-walkthrough
[node-api-reference]: https://docs.microsoft.com/en-us/javascript/api/azure-iothub/
[iothub-explorer]: https://docs.microsoft.com/en-us/azure/iot-pnp/howto-use-iot-explorer
[dm-patterns]: ../../../doc/dmpatterns.md
[debug-with-vscode]: ../../../doc/get_started/node-debug-vscode.md

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

@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var Client = require('azure-iothub').Client;
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var client = Client.fromConnectionString(connectionString);
client.open(function (err) {
if (err) {
console.error('Could not connect: ' + err.message);
} else {
console.log('Client connected');
client.getFileNotificationReceiver(function(err, receiver) {
if(err) {
console.error('Could not get file notification receiver: ' + err.message);
} else {
receiver.on('message', function(msg) {
console.log('File uploaded: ');
console.log(msg.data.toString());
receiver.complete(msg, function(err) {
if (err) {
console.error('Could not complete the message: ' + err.message);
} else {
console.log('Message completed');
}
});
});
}
});
}
});

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

@ -0,0 +1,92 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var uuid = require('uuid');
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var registry = iothub.Registry.fromConnectionString(connectionString);
// Specify the new devices.
var deviceAddArray = [
{
deviceId: 'Device1',
status: 'disabled',
authentication: {
symmetricKey: {
primaryKey: Buffer.from(uuid.v4()).toString('base64'),
secondaryKey: Buffer.from(uuid.v4()).toString('base64')
}
}
},
{
deviceId: 'Device2',
status: 'disabled',
authentication: {
symmetricKey: {
primaryKey: Buffer.from(uuid.v4()).toString('base64'),
secondaryKey: Buffer.from(uuid.v4()).toString('base64')
}
}
},
{
deviceId: 'Device3',
status: 'disabled',
authentication: {
symmetricKey: {
primaryKey: Buffer.from(uuid.v4()).toString('base64'),
secondaryKey: Buffer.from(uuid.v4()).toString('base64')
}
}
}
];
var deviceUpdateArray = [
{
deviceId: deviceAddArray[0].deviceId,
status: 'enabled'
},
{
deviceId: deviceAddArray[1].deviceId,
status: 'enabled'
},
{
deviceId: deviceAddArray[2].deviceId,
status: 'enabled'
}
];
var deviceRemoveArray = [
{
deviceId: deviceAddArray[0].deviceId
},
{
deviceId: deviceAddArray[1].deviceId
},
{
deviceId: deviceAddArray[2].deviceId
}
];
console.log('Adding devices: ' + JSON.stringify(deviceAddArray));
registry.addDevices(deviceAddArray, printAndContinue( 'adding', function next() {
registry.updateDevices(deviceUpdateArray, true, printAndContinue('updating', function next() {
registry.removeDevices(deviceRemoveArray, true, printAndContinue('removing'));
}));
}));
function printAndContinue(op, next) {
return function printResult(err, resultData) {
if (err) console.log(op + ' error: ' + err.toString());
if (resultData) {
var arrayString = resultData.errors.length === 0 ? 'no errors' : JSON.stringify(resultData.errors);
console.log(op + ' isSuccessful: ' + resultData.isSuccessful + ', errors returned: ' + arrayString);
}
if (next) next();
};
}

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

@ -0,0 +1,85 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var azureStorage = require('azure-storage');
var iothubConnectionString = process.env.IOTHUB_CONNECTION_STRING;
var storageConnectionString = process.env.STORAGE_CONNECTION_STRING;
var registry = iothub.Registry.fromConnectionString(iothubConnectionString);
var blobSvc = azureStorage.createBlobService(storageConnectionString);
var startDate = new Date();
var expiryDate = new Date(startDate);
expiryDate.setMinutes(startDate.getMinutes() + 100);
startDate.setMinutes(startDate.getMinutes() - 100);
var outputSharedAccessPolicy = {
AccessPolicy: {
Permissions: 'rwd',
Start: startDate,
Expiry: expiryDate
},
};
var inputContainerName = 'importcontainer';
var outputContainerName = 'exportcontainer';
var deviceFile = 'devices.txt';
blobSvc.createContainerIfNotExists(inputContainerName, function (error) {
if(error) {
console.error('Could not create input container: ' + error.message);
} else {
blobSvc.createBlockBlobFromLocalFile(inputContainerName, deviceFile, deviceFile, function (error) {
if (error) {
console.error('Could not create devices.txt: ' + error.message);
} else {
blobSvc.createContainerIfNotExists(outputContainerName, function (error) {
if (error) {
console.error('Could not create output container: ' + error.message);
} else {
var outputSasToken = blobSvc.generateSharedAccessSignature(outputContainerName, null, outputSharedAccessPolicy);
var outputSasUrl = blobSvc.getUrl(outputContainerName, null, outputSasToken);
/**
* There can only be one active job at a time, therefore, you can uncomment the export section and comment the import
* session or vice versa depending on what part of the code you would like to test.
*/
registry.exportDevicesToBlobByIdentity(outputSasUrl, false, function (error, result) {
if (error) {
console.error('Could not create export job: ' + error.message);
} else {
console.log('--------------\r\nDevices Export Job Identifier:--------------\r\n' + result);
var jobId = result.jobId;
var interval = setInterval(function () {
registry.getJob(jobId, function (error, result) {
if (error) {
console.error('Could not get job status: ' + error.message + ' : ' + error.responseBody);
} else {
console.log('--------------\r\njob ' + jobId + ' status:\r\n--------------\r\n' + result);
var status = result.status;
if (status === "completed") {
clearInterval(interval);
}
}
});
}, 500);
}
});
registry.listJobs(function(error, result) {
if (error) {
console.error('Could not list jobs: ' + error.message + ' : ' + error.responseBody);
} else {
console.log('Job list:\r\n----------\r\n' + result);
}
});
}
});
}
});
}
});

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

@ -0,0 +1,103 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var azureStorage = require('azure-storage');
var iothubConnectionString = process.env.IOTHUB_CONNECTION_STRING;
var storageConnectionString = process.env.STORAGE_CONNECTION_STRING;
var registry = iothub.Registry.fromConnectionString(iothubConnectionString);
var blobSvc = azureStorage.createBlobService(storageConnectionString);
var startDate = new Date();
var expiryDate = new Date(startDate);
expiryDate.setMinutes(startDate.getMinutes() + 100);
startDate.setMinutes(startDate.getMinutes() - 100);
var inputSharedAccessPolicy = {
AccessPolicy: {
Permissions: 'rl',
Start: startDate,
Expiry: expiryDate
},
};
var outputSharedAccessPolicy = {
AccessPolicy: {
Permissions: 'rwd',
Start: startDate,
Expiry: expiryDate
},
};
var inputContainerName = 'importcontainer';
var outputContainerName = 'exportcontainer';
var deviceFile = 'devices.txt';
blobSvc.createContainerIfNotExists(inputContainerName, function (error) {
if(error) {
console.error('Could not create input container: ' + error.message);
} else {
var inputSasToken = blobSvc.generateSharedAccessSignature(inputContainerName, null, inputSharedAccessPolicy);
var inputSasUrl = blobSvc.getUrl(inputContainerName, null, inputSasToken);
blobSvc.createBlockBlobFromLocalFile(inputContainerName, deviceFile, deviceFile, function (error) {
if (error) {
console.error('Could not create devices.txt: ' + error.message);
} else {
blobSvc.createContainerIfNotExists(outputContainerName, function (error) {
if (error) {
console.error('Could not create output container: ' + error.message);
} else {
var outputSasToken = blobSvc.generateSharedAccessSignature(outputContainerName, null, outputSharedAccessPolicy);
var outputSasUrl = blobSvc.getUrl(outputContainerName, null, outputSasToken);
registry.importDevicesFromBlobByIdentity(inputSasUrl, outputSasUrl, function (error, result) {
if (error) {
console.error('Could not create import devices: ' + error.message + ' : ' + error.responseBody);
} else {
console.log('--------------\r\nDevices Import Job Identifier:--------------\r\n' + result);
var jobId = result.jobId;
var interval = setInterval(function () {
/**
* Uncomment this code to test cancelling a job.
*/
// registry.cancelJob(jobId, function (error, result) {
// if (error) {
// console.error('Could not cancel job: ' + error.message + ' : ' + error.responseBody);
// } else {
// console.log('--------------\r\njob ' + jobId + ' cancelled:\r\n--------------\r\n' + result);
// clearInterval(interval);
// }
// });
registry.getJob(jobId, function (error, result) {
if (error) {
console.error('Could not get job status: ' + error.message + ' : ' + error.responseBody);
} else {
console.log('--------------\r\njob ' + jobId + ' status:\r\n--------------\r\n' + result);
var status = result.status;
if (status === "completed") {
clearInterval(interval);
}
}
});
}, 500);
}
});
registry.listJobs(function(error, result) {
if (error) {
console.error('Could not list jobs: ' + error.message + ' : ' + error.responseBody);
} else {
console.log('Job list:\r\n----------\r\n' + result);
}
});
}
});
}
});
}
});

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

@ -0,0 +1,131 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var azureStorage = require('azure-storage');
var iothubConnectionString = process.env.IOTHUB_CONNECTION_STRING;
var storageConnectionString = process.env.STORAGE_CONNECTION_STRING;
var registry = iothub.Registry.fromConnectionString(iothubConnectionString);
var blobSvc = azureStorage.createBlobService(storageConnectionString);
var startDate = new Date();
var expiryDate = new Date(startDate);
expiryDate.setMinutes(startDate.getMinutes() + 100);
startDate.setMinutes(startDate.getMinutes() - 100);
var inputSharedAccessPolicy = {
AccessPolicy: {
Permissions: 'rl',
Start: startDate,
Expiry: expiryDate
},
};
var outputSharedAccessPolicy = {
AccessPolicy: {
Permissions: 'rwd',
Start: startDate,
Expiry: expiryDate
},
};
var inputContainerName = 'importcontainer';
var outputContainerName = 'exportcontainer';
var deviceFile = 'devices.txt';
blobSvc.createContainerIfNotExists(inputContainerName, function (error) {
if(error) {
console.error('Could not create input container: ' + error.message);
} else {
var inputSasToken = blobSvc.generateSharedAccessSignature(inputContainerName, null, inputSharedAccessPolicy);
var inputSasUrl = blobSvc.getUrl(inputContainerName, null, inputSasToken);
blobSvc.createBlockBlobFromLocalFile(inputContainerName, deviceFile, deviceFile, function (error) {
if (error) {
console.error('Could not create devices.txt: ' + error.message);
} else {
blobSvc.createContainerIfNotExists(outputContainerName, function (error) {
if (error) {
console.error('Could not create output container: ' + error.message);
} else {
var outputSasToken = blobSvc.generateSharedAccessSignature(outputContainerName, null, outputSharedAccessPolicy);
var outputSasUrl = blobSvc.getUrl(outputContainerName, null, outputSasToken);
/**
* There can only be one active job at a time, therefore, you can uncomment the export section and comment the import
* session or vice versa depending on what part of the code you would like to test.
*/
// registry.exportDevicesToBlob(outputSasUrl, false, function (error, result) {
// if (error) {
// console.error('Could not create export job: ' + error.message);
// } else {
// console.log('--------------\r\nDevices Export Job Identifier:--------------\r\n' + result);
// var jobId = result.jobId;
// var interval = setInterval(function () {
// registry.getJob(jobId, function (error, result) {
// if (error) {
// console.error('Could not get job status: ' + error.message + ' : ' + error.responseBody);
// } else {
// console.log('--------------\r\njob ' + jobId + ' status:\r\n--------------\r\n' + result);
// var status = result.status;
// if (status === "completed") {
// clearInterval(interval);
// }
// }
// });
// }, 500);
// }
// });
registry.importDevicesFromBlob(inputSasUrl, outputSasUrl, function (error, result) {
if (error) {
console.error('Could not create import devices: ' + error.message + ' : ' + error.responseBody);
} else {
console.log('--------------\r\nDevices Import Job Identifier:--------------\r\n' + result);
var jobId = result.jobId;
var interval = setInterval(function () {
/**
* Uncomment this code to test cancelling a job.
*/
// registry.cancelJob(jobId, function (error, result) {
// if (error) {
// console.error('Could not cancel job: ' + error.message + ' : ' + error.responseBody);
// } else {
// console.log('--------------\r\njob ' + jobId + ' cancelled:\r\n--------------\r\n' + result);
// clearInterval(interval);
// }
// });
registry.getJob(jobId, function (error, result) {
if (error) {
console.error('Could not get job status: ' + error.message + ' : ' + error.responseBody);
} else {
console.log('--------------\r\njob ' + jobId + ' status:\r\n--------------\r\n' + result);
var status = result.status;
if (status === "completed") {
clearInterval(interval);
}
}
});
}, 500);
}
});
registry.listJobs(function(error, result) {
if (error) {
console.error('Could not list jobs: ' + error.message + ' : ' + error.responseBody);
} else {
console.log('Job list:\r\n----------\r\n' + result);
}
});
}
});
}
});
}
});

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

@ -0,0 +1,22 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var util = require('util');
var iothubConnectionString = process.env.IOTHUB_CONNECTION_STRING;
var outputContainerURI = "<URI to a container where a 'devices.txt' blob will be created.>";
var userAssignedManagedIdentity = "<The resource ID to a user assigned managed identity on the IoT Hub with access to the container.>";
var registry = iothub.Registry.fromConnectionString(iothubConnectionString);
registry.exportDevicesToBlobByIdentity(outputContainerURI, true, userAssignedManagedIdentity, function (error, result) {
if (error) {
console.error('Could not create export job: ' + error.message);
} else {
console.log('Result:\n' + util.inspect(result));
}
});

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

@ -0,0 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var util = require('util');
var iothubConnectionString = process.env.IOTHUB_CONNECTION_STRING;
var inputContainerURI = "<URI to a container with a blob named 'devices.txt' containing a list of devices to import.>";
var outputContainerURI = "<URI to a container where a blob will be created with logs of the import process.>";
var userAssignedManagedIdentity = "<The resource ID to a user assigned managed identity on the IoT Hub with access to the containers.>";
var registry = iothub.Registry.fromConnectionString(iothubConnectionString);
registry.importDevicesFromBlobByIdentity(inputContainerURI, outputContainerURI, userAssignedManagedIdentity, function (error, result) {
if (error) {
console.error('Could not create import job: ' + error.message);
} else {
console.log('Result:\n' + util.inspect(result));
}
});

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

@ -0,0 +1,45 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var iothub = require('azure-iothub');
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var registry = iothub.Registry.fromConnectionString(connectionString);
// List devices
console.log('**listing devices...');
registry.list(function (err, deviceList) {
deviceList.forEach(function (device) {
var key = device.authentication ? device.authentication.symmetricKey.primaryKey : '<no primary key>';
console.log(device.deviceId + ': ' + key);
});
// Create a new device
var device = {
deviceId: 'sample-device-' + Date.now()
};
console.log('\n**creating device \'' + device.deviceId + '\'');
registry.create(device, printAndContinue('create', function next() {
// Get the newly-created device
console.log('\n**getting device \'' + device.deviceId + '\'');
registry.get(device.deviceId, printAndContinue('get', function next() {
// Delete the new device
console.log('\n**deleting device \'' + device.deviceId + '\'');
registry.delete(device.deviceId, printAndContinue('delete'));
}));
}));
});
function printAndContinue(op, next) {
return function printResult(err, deviceInfo, res) {
if (err) console.log(op + ' error: ' + err.toString());
if (res) console.log(op + ' status: ' + res.statusCode + ' ' + res.statusMessage);
if (deviceInfo) console.log(op + ' device info: ' + JSON.stringify(deviceInfo));
if (next) next();
};
}

92
samples/schedule_job.js Normal file
Просмотреть файл

@ -0,0 +1,92 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
"use strict";
const uuid = require('uuid');
const JobClient = require('azure-iothub').JobClient;
const connectionString = process.env.IOTHUB_CONNECTION_STRING;
const queryCondition = 'deviceId IN [' +
process.env.IOTHUB_DEVICE_ID + ', ' +
process.env.IOTHUB_DEVICE_ID2 + ']'; // example queryCondition = "deviceId IN ['MyDevice1', 'MyDevice2']"
// For a single device you can also set queryCondition as "deviceId = '<device id>'" . Example, "deviceId = 'MyDevice1'"
const startTime = new Date();
const maxExecutionTimeInSeconds = 3600;
const jobClient = JobClient.fromConnectionString(connectionString);
// Schedule a device method call.
const methodParams = {
methodName: 'methodName',
payload: null,
responseTimeoutInSeconds: 15 // set response timeout as 15 seconds
};
const methodJobId = uuid.v4();
console.log('scheduling Device Method job with id: ' + methodJobId);
jobClient.scheduleDeviceMethod(methodJobId,
queryCondition,
methodParams,
startTime,
maxExecutionTimeInSeconds,
function(err) {
if (err) {
console.error('Could not schedule device method job: ' + err.message);
} else {
monitorJob(methodJobId, function(err, result) {
if (err) {
console.error('Could not monitor device method job: ' + err.message);
} else {
console.log(JSON.stringify(result, null, 2));
}
});
}
});
// Schedule a Twin update
var twinPatch = {
etag: '*',
tags: {
state: 'WA'
},
properties: {desired: {}, reported: {}}
};
var twinJobId = uuid.v4();
console.log('scheduling Twin Update job with id: ' + twinJobId);
jobClient.scheduleTwinUpdate(twinJobId,
queryCondition,
twinPatch,
startTime,
maxExecutionTimeInSeconds,
function(err) {
if (err) {
console.error('Could not schedule twin update job: ' + err.message);
} else {
monitorJob(twinJobId, function(err, result) {
if (err) {
console.error('Could not monitor twin update job: ' + err.message);
} else {
console.log(JSON.stringify(result, null, 2));
}
});
}
});
function monitorJob (jobId, callback) {
var jobMonitorInterval = setInterval(function() {
jobClient.getJob(jobId, function(err, result) {
if (err) {
console.error('Could not get job status: ' + err.message);
} else {
console.log('Job: ' + jobId + ' - status: ' + result.status);
if (result.status === 'completed' || result.status === 'failed' || result.status === 'cancelled') {
clearInterval(jobMonitorInterval);
callback(null, result);
}
}
});
}, 5000);
}

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

@ -0,0 +1,39 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var Client = require('azure-iothub').Client;
var Message = require('azure-iot-common').Message;
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var targetDevice = process.env.IOTHUB_DEVICE_ID;
var client = Client.fromConnectionString(connectionString);
client.open(function (err) {
if (err) {
console.error('Could not connect: ' + err.message);
} else {
console.log('Client connected');
// Create a message and send it to the IoT Hub every second
setInterval(function () {
var data = JSON.stringify({ text : 'foo' });
var message = new Message(data);
console.log('Sending message: ' + message.getData());
client.send(targetDevice, message, printResultFor('send'));
}, 2000);
}
});
// Helper function to print results in the console
function printResultFor(op) {
return function printResult(err, res) {
if (err) {
console.log(op + ' error: ' + err.toString());
} else {
console.log(op + ' status: ' + res.constructor.name);
}
};
}

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

@ -0,0 +1,46 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const { DefaultAzureCredential } = require("@azure/identity");
var Client = require('azure-iothub').Client;
var Message = require('azure-iot-common').Message;
var hostName = process.env.AZURE_AAD_HOST;
// DefaultAzureCredential expects the following three environment variables:
// - AZURE_TENANT_ID: The tenant ID in Azure Active Directory
// - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant
// - AZURE_CLIENT_SECRET: The client secret for the registered application
const credential = new DefaultAzureCredential();
var client = Client.fromTokenCredential(hostName, credential);
var targetDevice = process.env.IOTHUB_DEVICE_ID;
client.open(function (err) {
if (err) {
console.error('Could not connect: ' + err.message);
} else {
console.log('Client connected');
// Create a message and send it to the IoT Hub every second
setInterval(function () {
var data = JSON.stringify({ text : 'foo' });
var message = new Message(data);
console.log('Sending message: ' + message.getData());
client.send(targetDevice, message, printResultFor('send'));
}, 2000);
}
});
// Helper function to print results in the console
function printResultFor(op) {
return function printResult(err, res) {
if (err) {
console.log(op + ' error: ' + err.toString());
} else {
console.log(op + ' status: ' + res.constructor.name);
}
};
}

46
samples/twin.js Normal file
Просмотреть файл

@ -0,0 +1,46 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
"use strict";
var Registry = require('azure-iothub').Registry;
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var deviceId = process.env.IOTHUB_DEVICE_ID;
var registry = Registry.fromConnectionString(connectionString);
registry.getTwin(deviceId, function(err, twin) {
if (err) {
console.error(err.message);
} else {
console.log('Model Id: ' + twin.modelId);
console.log(JSON.stringify(twin, null, 2));
var twinPatch = {
tags: {
city: "Redmond"
},
properties: {
desired: {
telemetryInterval: 1000
}
}
};
// method 1: using the update method directly on the twin
twin.update(twinPatch, function(err, twin) {
if (err) {
console.error(err.message);
} else {
console.log(JSON.stringify(twin, null, 2));
// method 2: using the updateTwin method on the Registry object
registry.updateTwin(twin.deviceId, { properties: { desired: { telemetryInterval: 2000 }}}, twin.etag, function(err, twin) {
if (err) {
console.error(err.message);
} else {
console.log(JSON.stringify(twin, null, 2));
}
});
}
});
}
});

26
samples/twin_query.js Normal file
Просмотреть файл

@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
var Registry = require('azure-iothub').Registry;
var connectionString = process.env.IOTHUB_CONNECTION_STRING;
var registry = Registry.fromConnectionString(connectionString);
var query = registry.createQuery('SELECT * FROM devices', 100);
var onResults = function(err, results) {
if (err) {
console.error('Failed to fetch the results: ' + err.message);
} else {
// Do something with the results
results.forEach(function(twin) {
console.log(twin.deviceId);
});
if (query.hasMoreResults) {
query.nextAsTwin(onResults);
}
}
};
query.nextAsTwin(onResults);

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

@ -0,0 +1,46 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const IoTHubTokenCredentials = require('azure-iothub').IoTHubTokenCredentials;
const DigitalTwinClient = require('azure-iothub').DigitalTwinClient;
// Simple example of how to:
// - create a Digital Twin Client using the DigitalTwinClient constructor
// - create a patch for modifying the Digital Twin
// - update the Digital Twin with patch
//
// Preconditions:
// - Environment variables have to be set
// - Twin enabled device must exist on the ADT hub
async function main() {
const deviceId = process.env.IOTHUB_DEVICE_ID;
const connString = process.env.IOTHUB_CONNECTION_STRING;
// Create service client
const credentials = new IoTHubTokenCredentials(connString);
const digitalTwinClient = new DigitalTwinClient(credentials);
// Update digital twin and verify the update
// If you already have a component thermostat1:
// const patch = [{
// op: 'replace',
// path: '/thermostat1/targetTemperature',
// value: 42
// }];
const patch = [{
op: 'add',
path: '/targetTemperature',
value: 42
}];
await digitalTwinClient.updateDigitalTwin(deviceId, patch);
console.log('Patch has been succesfully applied');
}
main().catch((err) => {
console.log('error code: ', err.code);
console.log('error message: ', err.message);
console.log('error stack: ', err.stack);
});

623
src/amqp.ts Normal file
Просмотреть файл

@ -0,0 +1,623 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { EventEmitter } from 'events';
import * as dbg from 'debug';
import * as machina from 'machina';
import * as async from 'async';
import { anHourFromNow, errors, results, SharedAccessSignature, Message, callbackToPromise, tripleValueCallbackToPromise, Callback, endpoint } from 'azure-iot-common';
import { Amqp as Base, AmqpMessage, SenderLink, AmqpBaseTransportConfig } from 'azure-iot-amqp-base';
import { translateError } from './amqp_service_errors.js';
import { Client } from './client';
import { ServiceReceiver } from './service_receiver.js';
import { ResultWithIncomingMessage, IncomingMessageCallback, createResultWithIncomingMessage } from './interfaces.js';
import { AccessToken } from '@azure/core-http';
const debug = dbg('azure-iothub:Amqp');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('../package.json');
function handleResult(errorMessage: string, done: IncomingMessageCallback<any>): IncomingMessageCallback<any> {
return (err, result) => {
if (err) {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_018: [All asynchronous instance methods shall call the `done` callback with either no arguments or a first null argument and a second argument that is the result of the operation if the operation succeeded.]*/
done(translateError(errorMessage, err));
} else {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_017: [All asynchronous instance methods shall call the `done` callback with a single parameter that is derived from the standard Javascript `Error` object if the operation failed.]*/
done(null, result);
}
};
}
function getTranslatedError(err: Error, message: string): Error {
if (err instanceof errors.UnauthorizedError || err instanceof errors.NotConnectedError || err instanceof errors.DeviceNotFoundError) {
return err;
}
return translateError(message, err);
}
/**
* Transport class used by the [service client]{@link azure-iothub.Client} to connect to the Azure IoT hub using the AMQP protocol over a secure (TLS) socket.
* This class should not be used directly and instead be passed to one of the {@link azure-iothub.Client} factory methods: {@link azure-iothub.Client.fromConnectionString|fromConnectionString} or {@link azure-iothub.Client.fromSharedAccessSignature|fromSharedAccessSignature}.
*/
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_001: [The Amqp constructor shall accept a config object with those 4 properties:
host (string) the fully-qualified DNS hostname of an IoT Hub
keyName (string) the name of a key that can be used to communicate with the IoT Hub instance
sharedAccessSignature (string) the key associated with the key name.] */
export class Amqp extends EventEmitter implements Client.Transport {
/**
* @private
*/
protected _config: Client.TransportConfigOptions;
private _amqp: Base;
private _renewalTimeout: NodeJS.Timer;
private _renewalNumberOfMilliseconds: number = 2700000;
private _fsm: machina.Fsm;
private _c2dEndpoint: string = '/messages/devicebound';
private _c2dLink: SenderLink;
private _c2dErrorListener: (err: Error) => void;
private _feedbackEndpoint: string = '/messages/serviceBound/feedback';
private _feedbackReceiver: ServiceReceiver;
private _feedbackErrorListener: (err: Error) => void;
private _fileNotificationEndpoint: string = '/messages/serviceBound/filenotifications';
private _fileNotificationReceiver: ServiceReceiver;
private _fileNotificationErrorListener: (err: Error) => void;
private _bearerTokenPrefix: string = 'Bearer ';
/**
* @private
*/
constructor(config: Client.TransportConfigOptions, amqpBase?: Base) {
super();
this._amqp = amqpBase ? amqpBase : new Base(true);
this._config = config;
this._renewalTimeout = null;
this._amqp.setDisconnectHandler((err) => {
this._fsm.handle('amqpError', err);
});
this._c2dErrorListener = (err) => {
debug('Error on the C2D link: ' + err.toString());
this._c2dLink = null;
};
this._feedbackErrorListener = (err) => {
debug('Error on the message feedback link: ' + err.toString());
this._feedbackReceiver = null;
};
this._fileNotificationErrorListener = (err) => {
debug('Error on the file notification link: ' + err.toString());
this._fileNotificationReceiver = null;
};
this._fsm = new machina.Fsm({
namespace: 'azure-iothub:Amqp',
initialState: 'disconnected',
states: {
disconnected: {
_onEnter: (err, callback) => {
if (err) {
if (callback) {
callback(err);
} else {
this.emit('disconnect', err);
}
} else {
if (callback) {
callback();
}
}
},
connect: (callback) => {
this._fsm.transition('connecting', callback);
},
disconnect: (callback) => callback(),
send: (amqpMessage, deviceEndpoint, callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_025: [The `send` method shall connect and authenticate the transport if it is disconnected.]*/
this._fsm.handle('connect', (err) => {
if (err) {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_026: [The `send` method shall call its callback with an error if connecting and/or authenticating the transport fails.]*/
callback(err);
} else {
this._fsm.handle('send', amqpMessage, deviceEndpoint, callback);
}
});
},
getFeedbackReceiver: (callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_033: [The `getFeedbackReceiver` method shall connect and authenticate the transport if it is disconnected.]*/
this._fsm.handle('connect', (err) => {
if (err) {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_034: [The `getFeedbackReceiver` method shall call its callback with an error if the transport fails to connect or authenticate.]*/
callback(err);
} else {
this._fsm.handle('getFeedbackReceiver', callback);
}
});
},
getFileNotificationReceiver: (callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_036: [The `getFileNotificationReceiver` method shall connect and authenticate the transport if it is disconnected.]*/
this._fsm.handle('connect', (err) => {
if (err) {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_037: [The `getFileNotificationReceiver` method shall call its callback with an error if the transport fails to connect or authenticate.]*/
callback(err);
} else {
this._fsm.handle('getFileNotificationReceiver', callback);
}
});
},
updateSharedAccessSignature: (_updatedSAS, callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_032: [The `updateSharedAccessSignature` shall not establish a connection if the transport is disconnected, but should use the new shared access signature on the next manually initiated connection attempt.]*/
callback();
},
updateAccessToken: (_tokenValue, callback) => {
callback();
},
amqpError: (err) => {
debug('Late arriving error received while in disconnected state.');
if (err) {
debug(err.toString());
}
}
},
connecting: {
_onEnter: (callback) => {
const config: AmqpBaseTransportConfig = {
uri: this._getConnectionUri(),
userAgentString: packageJson.name + '/' + packageJson.version
};
debug('connecting');
this._amqp.connect(config, (err, _result) => {
if (err) {
debug('failed to connect' + err.toString());
this._fsm.transition('disconnected', err, callback);
} else {
debug('connected');
this._fsm.transition('authenticating', callback);
}
});
},
disconnect: (callback) => {
this._fsm.transition('disconnecting', null, callback);
},
amqpError: (err) => {
this._fsm.transition('disconnecting', err);
},
'*': () => this._fsm.deferUntilTransition()
},
authenticating: {
_onEnter: (callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_06_001: [`initializeCBS` shall be invoked.]*/
this._amqp.initializeCBS((err) => {
if (err) {
debug('error trying to initialize CBS: ' + err.toString());
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_06_002: [If `initializeCBS` is not successful then the client will remain disconnected and the callback, if provided, will be invoked with an error object.]*/
this._fsm.transition('disconnecting', err, callback);
} else {
debug('CBS initialized');
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_06_003: [If `initializeCBS` is successful, `putToken` shall be invoked with the first parameter audience, created from the sr of the sas signature, the next parameter of the actual sas, and a callback.]*/
if (this._config.sharedAccessSignature) {
const audience = SharedAccessSignature.parse(this._config.sharedAccessSignature.toString(), ['sr', 'sig', 'se']).sr;
const isApplicationSuppliedSas = typeof (this._config.sharedAccessSignature) === 'string';
const sasToken = isApplicationSuppliedSas ? this._config.sharedAccessSignature as string : (this._config.sharedAccessSignature as SharedAccessSignature).extend(anHourFromNow());
this._amqp.putToken(audience, sasToken, (err) => {
if (err) {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_06_004: [** If `putToken` is not successful then the client will remain disconnected and the callback, if provided, will be invoked with an error object.]*/
this._fsm.transition('disconnecting', err, callback);
} else {
this._fsm.transition('authenticated', isApplicationSuppliedSas, callback);
}
});
} else if (this._config.tokenCredential) {
this.getToken().then((accessToken) => {
const tokenValue = this._bearerTokenPrefix + accessToken.token;
this._amqp.putToken(this._config.tokenScope, tokenValue, (err) => {
if (err) {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_06_004: [** If `putToken` is not successful then the client will remain disconnected and the callback, if provided, will be invoked with an error object.]*/
this._fsm.transition('disconnecting', err, callback);
} else {
this._renewalNumberOfMilliseconds = Math.max(1000, (2 / 3) * (accessToken.expiresOnTimestamp - Date.now()));
this._fsm.transition('authenticated', false, callback);
}
});
}).catch((err) => {
this._fsm.transition('disconnecting', err, callback);
});
}
}
});
},
disconnect: (callback) => {
this._fsm.transition('disconnecting', null, callback);
},
amqpError: (err) => {
this._fsm.transition('disconnecting', err);
},
'*': () => this._fsm.deferUntilTransition()
},
authenticated: {
_onEnter: (isApplicationSuppliedSas, callback) => {
if (!isApplicationSuppliedSas) {
const renewalCallback = this._config.sharedAccessSignature ?
this._handleSASRenewal :
this._handleTokenCredentialRenewal;
this._renewalTimeout = setTimeout(renewalCallback.bind(this), this._renewalNumberOfMilliseconds);
}
callback(null, new results.Connected());
},
_onExit: (_callback) => {
if (this._renewalTimeout) {
clearTimeout(this._renewalTimeout);
}
},
connect: (callback) => callback(),
disconnect: (callback) => this._fsm.transition('disconnecting', null, callback),
send: (amqpMessage, deviceEndpoint, callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [The message generated by the `send` method should have its “to” field set to the device ID passed as an argument.]*/
amqpMessage.to = deviceEndpoint;
if (!this._c2dLink) {
debug('attaching new sender link: ' + this._c2dEndpoint);
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_027: [The `send` method shall attach the C2D link if necessary.]*/
this._amqp.attachSenderLink(this._c2dEndpoint, null, (err, link) => {
if (err) {
debug('error trying to attach new sender link: ' + err.toString());
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_029: [The `send` method shall call its callback with an error if it fails to attach the C2D link.]*/
callback(err);
} else {
debug('sender link attached. sending message.');
this._c2dLink = link;
this._c2dLink.on('error', this._c2dErrorListener);
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_030: [The `send` method shall call the `send` method of the C2D link and pass it the Amqp request that it created.]*/
this._c2dLink.send(amqpMessage, callback);
}
});
} else {
debug('reusing existing sender link: ' + this._c2dEndpoint + '. sending message.');
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_028: [The `send` method shall reuse the C2D link if it is already attached.]*/
this._c2dLink.send(amqpMessage, callback);
}
},
getFeedbackReceiver: (callback) => {
if (this._feedbackReceiver) {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_035: [The `getFeedbackReceiver` method shall reuse the existing feedback receiver it if has already been attached.]*/
callback(null, this._feedbackReceiver);
} else {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_013: [The `getFeedbackReceiver` method shall request an `AmqpReceiver` object from the base AMQP transport for the `/messages/serviceBound/feedback` endpoint.]*/
this._amqp.attachReceiverLink(this._feedbackEndpoint, null, (err, link) => {
if (err) {
callback(err);
} else {
this._feedbackReceiver = new ServiceReceiver(link);
this._feedbackReceiver.on('error', this._feedbackErrorListener);
callback(null, this._feedbackReceiver);
}
});
}
},
getFileNotificationReceiver: (callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_038: [The `getFileNotificationReceiver` method shall reuse the existing feedback receiver it if has already been attached.]*/
if (this._fileNotificationReceiver) {
callback(null, this._fileNotificationReceiver);
} else {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_016: [The `getFileNotificationReceiver` method shall request an `AmqpReceiver` object from the base AMQP transport for the `/messages/serviceBound/filenotifications` endpoint.]*/
this._amqp.attachReceiverLink(this._fileNotificationEndpoint, null, (err, link) => {
if (err) {
callback(err);
} else {
this._fileNotificationReceiver = new ServiceReceiver(link);
this._fileNotificationReceiver.on('error', this._fileNotificationErrorListener);
callback(null, this._fileNotificationReceiver);
}
});
}
},
updateSharedAccessSignature: (updatedSAS, callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_031: [The `updateSharedAccessSignature` shall trigger a `putToken` call on the base transport if it is connected.]*/
const audience = SharedAccessSignature.parse(this._config.sharedAccessSignature.toString(), ['sr', 'sig', 'se']).sr;
this._amqp.putToken(audience, updatedSAS, callback);
},
updateAccessToken: (tokenValue, callback) => {
this._amqp.putToken(this._config.tokenScope, tokenValue, callback);
},
amqpError: (err) => {
this._fsm.transition('disconnecting', err);
}
},
disconnecting: {
_onEnter: (err, disconnectCallback) => {
let finalError: Error = err;
async.series([
(callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_021: [The `disconnect` method shall detach the C2D messaging link if it is attached.]*/
if (this._c2dLink) {
const tmpC2DLink = this._c2dLink;
this._c2dLink = undefined;
if (err) {
debug('force-detaching c2d links');
tmpC2DLink.forceDetach(err);
callback();
} else {
tmpC2DLink.detach((detachErr) => {
if (detachErr) {
debug('error detaching the c2d link: ' + detachErr.toString());
if (!finalError) {
finalError = translateError('error while detaching the c2d link when disconnecting', detachErr);
}
} else {
debug('c2d link detached.');
}
callback();
});
}
} else {
callback();
}
},
(callback) => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_022: [The `disconnect` method shall detach the C2D feedback receiver link if it is attached.]*/
if (this._feedbackReceiver) {
const tmpFeedbackReceiver = this._feedbackReceiver;
this._feedbackReceiver = undefined;
if (err) {
tmpFeedbackReceiver.forceDetach(err);
tmpFeedbackReceiver.removeListener('error', this._feedbackErrorListener);
callback();
} else {
tmpFeedbackReceiver.detach((detachErr) => {
if (detachErr) {
debug('error detaching the message feedback link: ' + detachErr.toString());
} else {
debug('feedback link detached');
}
tmpFeedbackReceiver.removeListener('error', this._feedbackErrorListener);
if (!finalError && detachErr) {
finalError = translateError('error while detaching the message feedback link when disconnecting', detachErr);
}
callback();
});
}
} else {
callback();
}
},
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_023: [The `disconnect` method shall detach the file notification receiver link if it is attached.]*/
(callback) => {
if (this._fileNotificationReceiver) {
const tmpFileNotificationReceiver = this._fileNotificationReceiver;
this._fileNotificationReceiver = undefined;
if (err) {
tmpFileNotificationReceiver.forceDetach(err);
tmpFileNotificationReceiver.removeListener('error', this._fileNotificationErrorListener);
callback();
} else {
tmpFileNotificationReceiver.detach((detachErr) => {
if (detachErr) {
debug('error detaching the file upload notification link: ' + detachErr.toString());
} else {
debug('File notification link detached');
}
tmpFileNotificationReceiver.removeListener('error', this._fileNotificationErrorListener);
if (!finalError && detachErr) {
finalError = translateError('error while detaching the file upload notification link when disconnecting', detachErr);
}
callback();
});
}
} else {
callback();
}
},
(callback) => {
this._amqp.disconnect((disconnectErr) => {
if (disconnectErr) {
debug('error disconnecting the AMQP connection: ' + disconnectErr.toString());
} else {
debug('amqp connection successfully disconnected.');
}
if (!finalError && disconnectErr) {
finalError = translateError('error while disconnecting the AMQP connection', disconnectErr);
}
callback();
});
}
], () => {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_024: [Any error generated by detaching a link should be passed as the single argument of the callback of the `disconnect` method.]*/
this._fsm.transition('disconnected', finalError, disconnectCallback);
});
},
'*': () => this._fsm.deferUntilTransition()
}
}
});
}
/**
* @private
* @method module:azure-iothub.Amqp#connect
* @description Establishes a connection with the IoT Hub instance.
* @param {TripleValueCallback<results.Connected, IncomingMessage>} [done] Optional callback called when the connection is established of if an error happened.
* @returns {Promise<results.Disconnected> | void} Promise if no callback function was passed, void otherwise.
*/
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_019: [The `connect` method shall call the `connect` method of the base AMQP transport and translate its result to the caller into a transport-agnostic object.]*/
connect(done: Callback<results.Connected>): void;
connect(): Promise<results.Connected>;
connect(done?: Callback<results.Connected>): Promise<results.Connected> | void {
return callbackToPromise((_callback) => {
this._fsm.handle('connect', (err) => {
if (err) {
_callback(translateError('AMQP Transport: Could not connect', err));
} else {
_callback(null, new results.Connected());
}
});
}, done);
}
/**
* @private
* @method module:azure-iothub.Amqp#disconnect
* @description Disconnects the link to the IoT Hub instance.
* @param {Callback<results.Disconnected>} [done] Optional callback called when disconnected of if an error happened.
* @returns {Promise<results.Disconnected> | void} Promise if no callback function was passed, void otherwise.
*/
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_020: [** The `disconnect` method shall call the `disconnect` method of the base AMQP transport and translate its result to the caller into a transport-agnostic object.]*/
disconnect(done: Callback<results.Disconnected>): void;
disconnect(): Promise<results.Disconnected>;
disconnect(done?: Callback<results.Disconnected>): Promise<results.Disconnected> | void {
return callbackToPromise((_callback) => {
this._fsm.handle('disconnect', (err) => {
if (err) {
_callback(getTranslatedError(err, 'error while disconnecting'));
} else {
_callback(null, new results.Disconnected());
}
});
}, done);
}
/**
* @private
* @method module:azure-iothub.Amqp#send
* @description Sends a message to the IoT Hub.
* @param {Message} message The [message]{@linkcode module:common/message.Message}
* to be sent.
* @param {Function} [done] The optional callback to be invoked when `send`
* completes execution.
* @returns {Promise<ResultWithIncomingMessage<results.MessageEnqueued>> | void} Promise if no callback function was passed, void otherwise.
*/
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_002: [The send method shall construct an AMQP request using the message passed in argument as the body of the message.]*/
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [The message generated by the send method should have its “to” field set to "/devices/(uriEncode<deviceId>)/messages/devicebound".]*/
send(deviceId: string, message: Message, done: IncomingMessageCallback<results.MessageEnqueued>): void;
send(deviceId: string, message: Message): Promise<ResultWithIncomingMessage<results.MessageEnqueued>>;
send(deviceId: string, message: Message, done?: IncomingMessageCallback<results.MessageEnqueued>): Promise<ResultWithIncomingMessage<results.MessageEnqueued>> | void {
return tripleValueCallbackToPromise((_callback) => {
const deviceEndpoint = endpoint.deviceMessagePath(encodeURIComponent(deviceId));
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_002: [The `send` method shall construct an AMQP request using the message passed in argument as the body of the message.]*/
const amqpMessage = AmqpMessage.fromMessage(message);
this._fsm.handle('send', amqpMessage, deviceEndpoint, handleResult('AMQP Transport: Could not send message', _callback));
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, done);
}
/**
* @private
* @method module:azure-iothub.Amqp#getFeedbackReceiver
* @description Gets the {@linkcode AmqpReceiver} object that can be used to receive messages from the IoT Hub instance and accept/reject/release them.
* @param {Function} [done] Optional callback used to return the {@linkcode AmqpReceiver} object.
* @returns {Promise<ResultWithIncomingMessage<Client.ServiceReceiver>> | void} Promise if no callback function was passed, void otherwise.
*/
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_013: [The `getFeedbackReceiver` method shall request an `AmqpReceiver` object from the base AMQP transport for the `/messages/serviceBound/feedback` endpoint.]*/
getFeedbackReceiver(done: IncomingMessageCallback<Client.ServiceReceiver>): void;
getFeedbackReceiver(): Promise<ResultWithIncomingMessage<Client.ServiceReceiver>>;
getFeedbackReceiver(done?: IncomingMessageCallback<Client.ServiceReceiver>): Promise<ResultWithIncomingMessage<Client.ServiceReceiver>> | void {
return tripleValueCallbackToPromise((_callback) => {
this._fsm.handle('getFeedbackReceiver', handleResult('AMQP Transport: Could not get feedback receiver', _callback));
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, done);
}
/**
* @private
* @method module:azure-iothub.Amqp#getFileNotificationReceiver
* @description Gets the {@linkcode AmqpReceiver} object that can be used to receive messages from the IoT Hub instance and accept/reject/release them.
* @param {Function} [done] Optional callback used to return the {@linkcode AmqpReceiver} object.
* @returns {Promise<Client.ServiceReceiver> | void} Promise if no callback function was passed, void otherwise.
*/
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_016: [The `getFeedbackReceiver` method shall request an `AmqpReceiver` object from the base AMQP transport for the `/messages/serviceBound/filenotifications` endpoint.]*/
getFileNotificationReceiver(done: IncomingMessageCallback<Client.ServiceReceiver>): void;
getFileNotificationReceiver(): Promise<ResultWithIncomingMessage<Client.ServiceReceiver>>;
getFileNotificationReceiver(done?: IncomingMessageCallback<Client.ServiceReceiver>): Promise<ResultWithIncomingMessage<Client.ServiceReceiver>> | void {
return tripleValueCallbackToPromise((_callback) => {
this._fsm.handle('getFileNotificationReceiver', handleResult('AMQP Transport: Could not get file notification receiver', _callback));
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, done);
}
/**
* @private
* Updates the shared access signature and puts a new CBS token.
* @param sharedAccessSignature New shared access signature used to put a new CBS token.
* @param [callback] Optional function called when the callback has been successfully called.
* @returns {Promise<results.SharedAccessSignatureUpdated> | void} Promise if no callback function was passed, void otherwise.
*/
updateSharedAccessSignature(sharedAccessSignature: string, callback: Callback<results.SharedAccessSignatureUpdated>): void;
updateSharedAccessSignature(sharedAccessSignature: string): Promise<results.SharedAccessSignatureUpdated>;
updateSharedAccessSignature(sharedAccessSignature: string, callback?: Callback<results.SharedAccessSignatureUpdated>): Promise<results.SharedAccessSignatureUpdated> | void {
return callbackToPromise((_callback) => {
if (!sharedAccessSignature) {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_039: [The `updateSharedAccessSignature` shall throw a `ReferenceError` if the `sharedAccessSignature` argument is falsy.]*/
throw new ReferenceError('sharedAccessSignature cannot be \'' + sharedAccessSignature + '\'');
}
this._config.sharedAccessSignature = sharedAccessSignature;
this._fsm.handle('updateSharedAccessSignature', sharedAccessSignature, (err) => {
if (err) {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_017: [** All asynchronous instance methods shall call the `_callback` callback with a single parameter that is derived from the standard Javascript `Error` object if the operation failed.]*/
_callback(err);
} else {
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_16_018: [All asynchronous instance methods shall call the `_callback` callback with either no arguments or a first null argument and a second argument that is the result of the operation if the operation succeeded.]*/
_callback(null, new results.SharedAccessSignatureUpdated(false));
}
});
}, callback);
}
/**
* @private
* Gets the AccessToken from the TokenCredential object.
* Throws if the TokenCredential object gives a null value.
* @returns {Promise<AccessToken>} The access token string.
*/
async getToken(): Promise<AccessToken> {
const accessToken = await this._config.tokenCredential.getToken(this._config.tokenScope);
if (!accessToken) {
throw new Error('AccessToken creation failed');
}
return accessToken;
}
protected _getConnectionUri(): string {
return 'amqps://' + this._config.host;
}
private _handleSASRenewal(): void {
const newSas = (this._config.sharedAccessSignature as SharedAccessSignature).extend(anHourFromNow());
this._fsm.handle('updateSharedAccessSignature', newSas, (err) => {
if (err) {
debug('error automatically renewing the sas token: ' + err.toString());
} else {
this._renewalTimeout = setTimeout(this._handleSASRenewal.bind(this), this._renewalNumberOfMilliseconds);
}
});
}
private _handleTokenCredentialRenewal(): void {
this.getToken().then((accessToken) => {
const tokenValue = this._bearerTokenPrefix + accessToken.token;
this._fsm.handle('updateAccessToken', tokenValue, (err) => {
if (err) {
debug('error automatically renewing the AccessToken');
} else {
this._renewalNumberOfMilliseconds = Math.max(1000, (2 / 3) * (accessToken.expiresOnTimestamp - Date.now()));
this._renewalTimeout = setTimeout(this._handleTokenCredentialRenewal.bind(this), this._renewalNumberOfMilliseconds);
}
});
}).catch((err) => {
debug('error getting a new AccessToken: ' + err.toString());
});
}
}

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

@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { errors } from 'azure-iot-common';
import { translateError as translateCommonError } from 'azure-iot-amqp-base';
import { AmqpError } from 'rhea';
/**
* @private
*/
export interface AmqpTransportError extends Error {
amqpError?: Error;
}
/*Codes_SRS_NODE_DEVICE_AMQP_COMMON_ERRORS_16_010: [ `translateError` shall accept 2 argument:
*- A custom error message to give context to the user.
*- the AMQP error object itself]
*/
/**
* @private
*/
export function translateError(message: string, amqpError: Error): AmqpTransportError {
let error: AmqpTransportError;
/*Codes_SRS_NODE_DEVICE_AMQP_SERVICE_ERRORS_16_001: [ `translateError` shall return an `DeviceMaximumQueueDepthExceededError` if the AMQP error condition is `amqp:resource-limit-exceeded`.] */
if ((amqpError as AmqpError).condition === 'amqp:resource-limit-exceeded') {
error = new errors.DeviceMaximumQueueDepthExceededError(message);
} else {
error = translateCommonError(message, amqpError);
}
error.amqpError = amqpError;
return error;
}

29
src/amqp_ws.ts Normal file
Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { Amqp } from './amqp';
import { Client } from './client';
/**
* Transport class used by the [service client]{@link azure-iothub.Client} to connect to the Azure IoT hub using the AMQP protocol over secure websockets.
* This class should not be used directly and instead be passed to one of the {@link azure-iothub.Client} factory methods: {@link azure-iothub.Client.fromConnectionString|fromConnectionString} or {@link azure-iothub.Client.fromSharedAccessSignature|fromSharedAccessSignature}.
*/
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_WS_16_001: [The `AmqpWs` constructor shall accept a config object with those four properties:
- `host` (string) the fully-qualified DNS hostname of an IoT Hub
- `keyName` (string) the name of a key that can be used to communicate with the IoT Hub instance
- `sharedAccessSignature–` (string) the key associated with the key name.]*/
/*Codes_SRS_NODE_IOTHUB_SERVICE_AMQP_WS_16_002: [`AmqpWs` should inherit from `Amqp`.]*/
export class AmqpWs extends Amqp implements Client.Transport {
/**
* @private
*/
constructor(config: Client.TransportConfigOptions) {
super(config);
}
protected _getConnectionUri(): string {
return 'wss://' + this._config.host + ':443/$iothub/websocket';
}
}

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

@ -0,0 +1,37 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
import { ServiceClientCredentials, WebResource, Constants } from '@azure/ms-rest-js';
import { ConnectionString, SharedAccessSignature, anHourFromNow } from 'azure-iot-common';
/**
* Creates shared access signatures based on the connection string passed to the constructor.
* This class is used by the protocol layer of the SDK to add authentication headers to each request.
*/
export class IoTHubTokenCredentials implements ServiceClientCredentials {
private _connectionString: ConnectionString;
constructor(connectionString: string) {
this._connectionString = ConnectionString.parse(connectionString, ['HostName', 'SharedAccessKeyName', 'SharedAccessKey']);
}
/**
* Adds an authorization header to the request object.
*
* @param webResource The request object that needs its authorization header populated
*/
signRequest(webResource: WebResource): Promise<WebResource> {
const sas = SharedAccessSignature.create(this._connectionString.HostName as string, this._connectionString.SharedAccessKeyName as string, this._connectionString.SharedAccessKey as string, anHourFromNow()).toString();
webResource.headers.set(Constants.HeaderConstants.AUTHORIZATION, sas);
return Promise.resolve(webResource);
}
/**
* Gets the Azure IoT Hub instance name from the connection string
*/
getHubName(): string {
return this._connectionString.HostName as string;
}
}

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

@ -0,0 +1,182 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
import { IotHubGatewayServiceAPIs as PLClient, IotHubGatewayServiceAPIsModels as Models } from '../pl/iotHubGatewayServiceAPIs';
import { tripleValueCallbackToPromise, TripleValueCallback } from 'azure-iot-common';
import { IoTHubTokenCredentials } from '../auth/iothub_token_credentials';
import * as msRest from '@azure/ms-rest-js';
/**
* @export
* @type DigitalTwin Type alias to simplify the auto generated type's name
*/
export type DigitalTwin = Models.DigitalTwinGetDigitalTwinHeaders | undefined;
/**
* @export
* @type DigitalTwinResponse Type alias to simplify the auto generated type's name
*/
export type DigitalTwinResponse = Models.DigitalTwinGetDigitalTwinResponse | undefined;
/**
* @export
* @type DigitalTwinUpdateResponse Type alias to simplify the auto generated type's name
*/
export type DigitalTwinUpdateResponse = Models.DigitalTwinUpdateDigitalTwinResponse | undefined;
/**
* @export
* @type DigitalTwinInvokeComponentCommandResponse Type alias to simplify the auto generated type's name
*/
export type DigitalTwinInvokeComponentCommandResponse = Models.DigitalTwinInvokeComponentCommandResponse | undefined;
/**
* @export
* @type DigitalTwinInvokeRootLevelCommandResponse Type alias to simplify the auto generated type's name
*/
export type DigitalTwinInvokeRootLevelCommandResponse = Models.DigitalTwinInvokeRootLevelCommandResponse | undefined;
/**
* @export
* @type DigitalTwinInvokeComponentCommandOptionalParams Type alias to simplify the auto generated type's name
*/
export type DigitalTwinInvokeComponentCommandOptionalParams = Models.DigitalTwinInvokeComponentCommandOptionalParams;
/**
* @export
* @type DigitalTwinInvokeRootLevelCommandOptionalParams Type alias to simplify the auto generated type's name
*/
export type DigitalTwinInvokeRootLevelCommandOptionalParams = Models.DigitalTwinInvokeRootLevelCommandOptionalParams;
/**
* @private
* Helper function to create extended result type
*/
function createResultWithHttpOperationResponse<TArg, TResult>(result: TArg, response: msRest.HttpOperationResponse): TResult {
if (result) {
(result as any)._response = response;
}
return <any>result;
}
/**
* @export
* @class DigitalTwinClient Main class to implement Azure IoT Digital Twin Client API
*/
export class DigitalTwinClient {
/**
* @private
* The IoTHub token credentials used for creating the Protocol Layer client.
*/
private _creds: IoTHubTokenCredentials;
/**
* @private
* The Protocol Layer Client instance used by the DigitalTwinClient.
*/
private _pl: PLClient;
/**
* @private
* The Azure IoT service's API version.
*/
private _apiVersion: string = '2021-04-12';
/**
* Constructor which also creates an instance of the Protocol Layer Client used by the DigitalTwinClient.
*
* @param {IoTHubTokenCredentials} creds The IoTHub token credentials used for creating the Protocol Layer client.
* @memberof DigitalTwinClient
*/
constructor(creds: IoTHubTokenCredentials) {
/*Code_SRS_NODE_DIGITAL_TWIN_CLIENT_12_001: [** The `DigitalTwinClient` creates an instance of the DigitalTwinClient passing IoTHubTokenCredentials class as an argument.]*/
this._creds = creds;
this._pl = new PLClient(this._creds, {
baseUri: 'https://' + this._creds.getHubName(),
apiVersion: this._apiVersion,
deserializationContentTypes: { // application/ld+json isn't supported by autorest by default, which is why we need these options
json: [
'application/ld+json',
'application/json',
'text/json'
]
}
});
}
/**
* @method getDigitalTwin module: azure-iot-digitaltwins-service.DigitalTwinClient.getDigitalTwin
* @description Retrieve the Digital Twin of a given device.
* @param {string} digitalTwinId The digital twin Id of the given device or module.
* Format of digitalTwinId is DeviceId[~ModuleId]. ModuleId is optional.
* @returns DigitalTwinResponse The return object containing the Digital Twin plus the HttpResponse.
* @memberof DigitalTwinClient
*/
getDigitalTwin(digitalTwinId: string): Promise<DigitalTwinResponse>;
getDigitalTwin(digitalTwinId: string, callback: TripleValueCallback<DigitalTwin, msRest.HttpOperationResponse>): void;
getDigitalTwin(digitalTwinId: string, callback?: TripleValueCallback<DigitalTwin, msRest.HttpOperationResponse>): void | Promise<DigitalTwinResponse> {
/*Codes_SRS_NODE_DIGITAL_TWIN_CLIENT_12_002: [The `getDigitalTwin` method shall call the `getDigitalTwin` method of the protocol layer with the given argument.]*/
/*Codes_SRS_NODE_DIGITAL_TWIN_CLIENT_12_003: [The `getDigitalTwin` method shall call the callback with an error parameter if a callback is passed..]*/
/*Codes_SRS_NODE_DIGITAL_TWIN_CLIENT_12_004: [The `getDigitalTwin` method shall return error if the method of the protocol layer failed.]*/
/*Codes_SRS_NODE_DIGITAL_TWIN_CLIENT_12_020: [The `getDigitalTwin` method shall return a promise if there is no callback passed.]*/
return tripleValueCallbackToPromise<DigitalTwin, msRest.HttpOperationResponse, DigitalTwinResponse>((_callback) => {
this._pl.digitalTwin.getDigitalTwin(digitalTwinId, (err, result, _request, response) => {
_callback(err as Error, result, response);
});
}, (result, response) => createResultWithHttpOperationResponse<DigitalTwin, DigitalTwinResponse>(result, response), callback as TripleValueCallback<DigitalTwin, msRest.HttpOperationResponse>);
}
/**
* @method updateDigitalTwin module: azure-iot-digitaltwins-service.DigitalTwinClient.updateDigitalTwin
* @description Update the Digital Twin of a given device using a patch object.
* @param {string} digitalTwinId The digital twin Id of the given device.
* @param {any[]} patch The patch objet contains the update part of a Digital Twin.
* @param {string} eTag The eTag for identifying the patch.
* @returns DigitalTwinUpdateResponse The HTTP response.
* @memberof DigitalTwinClient
*/
updateDigitalTwin(digitalTwinId: string, patch: any[], eTag?: string): Promise<DigitalTwinUpdateResponse>;
updateDigitalTwin(digitalTwinId: string, patch: any[], eTagOrCallback?: string | TripleValueCallback<void, msRest.HttpOperationResponse>, callback?: TripleValueCallback<void, msRest.HttpOperationResponse>): void;
updateDigitalTwin(digitalTwinId: string, patch: any[], eTagOrCallback?: string | TripleValueCallback<void, msRest.HttpOperationResponse>, callback?: TripleValueCallback<void, msRest.HttpOperationResponse>): void | Promise<DigitalTwinUpdateResponse> {
const actualCallback = typeof eTagOrCallback === 'function' ? eTagOrCallback : callback;
const actualEtag = typeof eTagOrCallback !== 'function' ? eTagOrCallback : undefined;
const options = actualEtag ? { ifMatch: actualEtag } : undefined;
return tripleValueCallbackToPromise<void, msRest.HttpOperationResponse, DigitalTwinUpdateResponse>((_callback) => {
this._pl.digitalTwin.updateDigitalTwin(digitalTwinId, patch, options as Models.DigitalTwinUpdateDigitalTwinOptionalParams, (err, result, _request, response) => {
_callback(err as Error, result, response);
});
}, (result, response) => createResultWithHttpOperationResponse<void, DigitalTwinUpdateResponse>(result, response), actualCallback as TripleValueCallback<void, msRest.HttpOperationResponse>);
}
/**
* @method invokeComponentCommand module: azure-iot-digitaltwins-service.DigitalTwinClient.invokeComponentCommand
* @description Invoke a command on an component of a particular device and get the result of it.
* @param {string} digitalTwinId The digital twin Id of the given device.
* @param {string} componentName The component's name.
* @param {string} commandName The command's name.
* @param {string|any} argument The argument of a command.
* @param {DigitalTwinInvokeComponentCommandOptionalParams} options The optional parameter to set options including connectionTimeoutInSeconds and responseTimeoutInSeconds.
* The responseTimeoutInSeconds must be within [5; 300]
* @returns DigitalTwinInvokeComponentCommandResponse The result of the invoked command containing the result, status code, request ID and the parsed HttpResponse.
* @memberof DigitalTwinClient
*/
invokeComponentCommand(digitalTwinId: string, componentName: string, commandName: string, argument: string|any, options?: DigitalTwinInvokeComponentCommandOptionalParams): Promise<DigitalTwinInvokeComponentCommandResponse>{
return this._pl.digitalTwin.invokeComponentCommand(digitalTwinId, componentName, commandName, argument, options);
}
/**
* @method invokeCommand module: azure-iot-digitaltwins-service.DigitalTwinClient.invokeCommand
* @description Invoke a command on an component of a particular device and get the result of it.
* @param {string} digitalTwinId The digital twin Id of the given device.
* @param {string|any} argument The argument of a command.
* @param {DigitalTwinInvokeRootLevelCommandOptionalParams} options The optional parameter to set options including connectionTimeoutInSeconds and responseTimeoutInSeconds.
* The responseTimeoutInSeconds must be within [5; 300]
* @returns DigitalTwinInvokeRootLevelCommandResponse The result of the invoked command containing the result, status code, request ID and the parsed HttpResponse.
* @memberof DigitalTwinClient
*/
invokeCommand(digitalTwinId: string, commandName: string, argument: string|any, options?: DigitalTwinInvokeRootLevelCommandOptionalParams): Promise<DigitalTwinInvokeRootLevelCommandResponse>{
return this._pl.digitalTwin.invokeRootLevelCommand(digitalTwinId, commandName, argument, options);
}
}

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

@ -0,0 +1,780 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
import { IotHubGatewayServiceAPIs as PLClient, IotHubGatewayServiceAPIsModels as Models } from '../pl/iotHubGatewayServiceAPIs';
import { IoTHubTokenCredentials } from '../auth/iothub_token_credentials';
import {
AuthenticationMechanism,
SymmetricKey,
X509Thumbprint,
DeviceCapabilities,
DevicesCreateOrUpdateIdentityOptionalParams,
DevicesDeleteIdentityOptionalParams,
ModulesCreateOrUpdateIdentityOptionalParams,
} from '../pl/models';
/**
* @export
* @type Device Type alias to simplify the auto generated type's name
*/
export type Device = Models.Device;
/**
* @export
* @type Module Type alias to simplify the auto generated type's name
*/
export type Module = Models.Module;
/**
* @export
* @type DevicesGetIdentityResponse Type alias to simplify the auto generated type's name
*/
export type DevicesGetIdentityResponse = Models.DevicesGetIdentityResponse;
/**
* @export
* @type DevicesCreateOrUpdateIdentityResponse Type alias to simplify the auto generated type's name
*/
export type DevicesCreateOrUpdateIdentityResponse = Models.DevicesCreateOrUpdateIdentityResponse;
/**
* @export
* @type DevicesGetDevicesResponse Type alias to simplify the auto generated type's name
*/
export type DevicesGetDevicesResponse = Models.DevicesGetDevicesResponse;
/**
* @export
* @type ModulesCreateOrUpdateIdentityResponse Type alias to simplify the auto generated type's name
*/
export type ModulesCreateOrUpdateIdentityResponse = Models.ModulesCreateOrUpdateIdentityResponse;
/**
* @export
* @type ModulesGetIdentityResponse Type alias to simplify the auto generated type's name
*/
export type ModulesGetIdentityResponse = Models.ModulesGetIdentityResponse;
/**
* @export
* @type ModulesGetModulesOnDeviceResponse Type alias to simplify the auto generated type's name
*/
export type ModulesGetModulesOnDeviceResponse = Models.ModulesGetModulesOnDeviceResponse;
/**
* @export
* @type ModulesDeleteIdentityOptionalParams Type alias to simplify the auto generated type's name
*/
export type ModulesDeleteIdentityOptionalParams = Models.ModulesDeleteIdentityOptionalParams;
/**
* @export
* @type StatisticsGetServiceStatisticsResponse Type alias to simplify the auto generated type's name
*/
export type StatisticsGetServiceStatisticsResponse = Models.StatisticsGetServiceStatisticsResponse;
/**
* @export
* @type StatisticsGetDeviceStatisticsResponse Type alias to simplify the auto generated type's name
*/
export type StatisticsGetDeviceStatisticsResponse = Models.StatisticsGetDeviceStatisticsResponse;
/**
* @export
* @type DevicesGetDevicesOptionalParams Type alias to simplify the auto generated type's name
*/
export type DevicesGetDevicesOptionalParams = Models.DevicesGetDevicesOptionalParams;
/**
* @export
* @type BulkRegistryUpdateRegistryResponse Type alias to simplify the auto generated type's name
*/
export type BulkRegistryUpdateRegistryResponse = Models.BulkRegistryUpdateRegistryResponse;
/**
* @export
* @type ExportImportDevice Type alias to simplify the auto generated type's name
*/
export type ExportImportDevice = Models.ExportImportDevice;
/**
* @export
* @type QuerySpecification Type alias to simplify the auto generated type's name
*/
export type QuerySpecification = Models.QuerySpecification;
/**
* @export
* @type QueryGetTwinsResponse Type alias to simplify the auto generated type's name
*/
export type QueryGetTwinsResponse = Models.QueryGetTwinsResponse;
/**
* @export
* @type DevicesGetTwinResponse Type alias to simplify the auto generated type's name
*/
export type DevicesGetTwinResponse = Models.DevicesGetTwinResponse;
/**
* @export
* @type Twin Type alias to simplify the auto generated type's name
*/
export type Twin = Models.Twin;
/**
* @export
* @type DevicesReplaceTwinResponse Type alias to simplify the auto generated type's name
*/
export type DevicesReplaceTwinResponse = Models.DevicesReplaceTwinResponse;
/**
* @export
* @type DevicesUpdateTwinOptionalParams Type alias to simplify the auto generated type's name
*/
export type DevicesUpdateTwinOptionalParams = Models.DevicesUpdateTwinOptionalParams;
/**
* @export
* @type ModulesGetTwinResponse Type alias to simplify the auto generated type's name
*/
export type ModulesGetTwinResponse = Models.ModulesGetTwinResponse;
/**
* @export
* @type DevicesUpdateTwinResponse Type alias to simplify the auto generated type's name
*/
export type DevicesUpdateTwinResponse = Models.DevicesUpdateTwinResponse;
/**
* @export
* @type ModulesReplaceTwinResponse Type alias to simplify the auto generated type's name
*/
export type ModulesReplaceTwinResponse = Models.ModulesReplaceTwinResponse;
/**
* @export
* @type ModulesUpdateTwinOptionalParams Type alias to simplify the auto generated type's name
*/
export type ModulesUpdateTwinOptionalParams = Models.ModulesUpdateTwinOptionalParams;
/**
* @export
* @type CloudToDeviceMethod Type alias to simplify the auto generated type's name
*/
export type CloudToDeviceMethod = Models.CloudToDeviceMethod;
/**
* @export
* @type DevicesInvokeMethodResponse Type alias to simplify the auto generated type's name
*/
export type DevicesInvokeMethodResponse = Models.DevicesInvokeMethodResponse;
/**
* @export
* @class IoTHubRegistryManager Main class to implement IoTHub Registry Manager Operations
* based on the top of the auto generated IotHub REST APIs
*/
export class IoTHubRegistryManager {
/**
* @private
* The IoTHub token credentials used for creating the Protocol Layer client.
*/
private _creds: IoTHubTokenCredentials;
/**
* @private
* The Protocol Layer Client instance used by the DigitalTwinClient.
*/
private _pl: PLClient;
/**
* @private
* The Azure IoT service's API version.
*/
private _apiVersion: string = '2021-04-12';
/**
* Constructor which also creates an instance of the Protocol Layer Client used by the DigitalTwinClient.
*
* @param {IoTHubTokenCredentials} creds The IoTHub token credentials used for creating the Protocol Layer client.
* @memberof DigitalTwinClient
*/
constructor(creds: IoTHubTokenCredentials) {
/*Code_SRS_NODE_DIGITAL_TWIN_CLIENT_12_001: [** The `DigitalTwinClient` creates an instance of the DigitalTwinClient passing IoTHubTokenCredentials class as an argument.]*/
this._creds = creds;
this._pl = new PLClient(this._creds, {
baseUri: 'https://' + this._creds.getHubName(),
apiVersion: this._apiVersion,
deserializationContentTypes: { // application/ld+json isn't supported by autorest by default, which is why we need these options
json: [
'application/ld+json',
'application/json',
'text/json'
]
}
});
}
/**
* @method createDeviceWithSas module: azure-iot-digitaltwins-service.IoTHubRegistryManager.createDeviceWithSas
* @description Creates a device identity on IoTHub using SAS authentication.
* @param {string} deviceId The name (Id) of the device.
* @param {string} primaryKey Primary authentication key.
* @param {string} secondaryKey Secondary authentication key.
* @param {boolean} isEnabled The status of the device. If the status disabled, a device cannot connect to the service.
* @returns Promise<DevicesCreateOrUpdateIdentityResponse> The return object containing the created device and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
createDeviceWithSas(
deviceId: string,
primaryKey: string,
secondaryKey: string,
isEnabled: boolean
): Promise<DevicesCreateOrUpdateIdentityResponse> {
const symmetricKey: SymmetricKey = {
primaryKey: primaryKey,
secondaryKey: secondaryKey
};
const authenticationMechanism: AuthenticationMechanism = {
type: 'sas',
symmetricKey: symmetricKey
};
const device: Device = {};
device.deviceId = deviceId;
if (isEnabled) {
device.status = 'enabled';
} else {
device.status = 'disabled';
}
device.authentication = authenticationMechanism;
return this._pl.devices.createOrUpdateIdentity(deviceId, device);
}
/**
* @method createDeviceWithX509 module: azure-iot-digitaltwins-service.IoTHubRegistryManager.createDeviceWithX509
* @description Creates a device identity on IoTHub using X509 authentication.
* @param {string} deviceId The name (Id) of the device.
* @param {string} primaryThumbprint Primary X509 thumbprint.
* @param {string} secondaryThumbprint Secondary X509 thumbprint.
* @param {boolean} isEnabled The status of the device. If the status disabled, a device cannot connect to the service.
* @param {boolean} iotEdge The device is part of a IoTEdge or not.
* @returns Promise<DevicesCreateOrUpdateIdentityResponse> The return object containing the created device and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
createDeviceWithX509(
deviceId: string,
primaryThumbprint: string,
secondaryThumbprint: string,
isEnabled: boolean,
iotEdge: boolean = false
): Promise<DevicesCreateOrUpdateIdentityResponse> {
const x509Thumbprint: X509Thumbprint = {
primaryThumbprint: primaryThumbprint,
secondaryThumbprint: secondaryThumbprint
};
const authenticationMechanism: AuthenticationMechanism = {
type: 'selfSigned',
x509Thumbprint: x509Thumbprint
};
const deviceCapabilities: DeviceCapabilities = {
iotEdge: iotEdge
};
const device: Device = {};
device.deviceId = deviceId;
if (isEnabled) {
device.status = 'enabled';
} else {
device.status = 'disabled';
}
device.authentication = authenticationMechanism;
device.capabilities = deviceCapabilities;
return this._pl.devices.createOrUpdateIdentity(deviceId, device);
}
/**
* @method createDeviceWithCertificateAuthority module: azure-iot-digitaltwins-service.IoTHubRegistryManager.createDeviceWithCertificateAuthority
* @description Creates a device identity on IoTHub using X509 authentication.
* @param {string} deviceId The name (Id) of the device.
* @param {boolean} isEnabled The status of the device. If the status disabled, a device cannot connect to the service.
* @param {boolean} iotEdge The device is part of a IoTEdge or not.
* @returns Promise<DevicesCreateOrUpdateIdentityResponse> The return object containing the created device and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
createDeviceWithCertificateAuthority(
deviceId: string,
isEnabled: boolean,
iotEdge: boolean = false
): Promise<DevicesCreateOrUpdateIdentityResponse> {
const authenticationMechanism: AuthenticationMechanism = {
type: 'certificateAuthority'
};
const deviceCapabilities: DeviceCapabilities = {
iotEdge: iotEdge
};
const device: Device = {};
device.deviceId = deviceId;
if (isEnabled) {
device.status = 'enabled';
} else {
device.status = 'disabled';
}
device.authentication = authenticationMechanism;
device.capabilities = deviceCapabilities;
return this._pl.devices.createOrUpdateIdentity(deviceId, device);
}
/**
* @method updateDevice module: azure-iot-digitaltwins-service.IoTHubRegistryManager.updateDevice
* @description Updates a device identity on IoTHub
* @param {string} deviceId The name (Id) of the device.
* @param {Device} device The contents of the device identity.
* @returns Promise<DevicesCreateOrUpdateIdentityResponse> The return object containing the updated device and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
updateDevice(
deviceId: string,
device: Device
): Promise<DevicesCreateOrUpdateIdentityResponse> {
const options: DevicesCreateOrUpdateIdentityOptionalParams = {
ifMatch: '*'
};
return this._pl.devices.createOrUpdateIdentity(deviceId, device, options);
}
/**
* @method getDevice module: azure-iot-digitaltwins-service.IoTHubRegistryManager.getDevice
* @description Retrieves a device identity from IoTHub.
* @param {string} deviceId The name (Id) of the device.
* @returns Promise<DevicesGetIdentityResponse> The return object containing the device and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
getDevice(
deviceId: string
): Promise<DevicesGetIdentityResponse> {
return this._pl.devices.getIdentity(deviceId);
}
/**
* @method deleteDevice module: azure-iot-digitaltwins-service.IoTHubRegistryManager.deleteDevice
* @description Deletes a device identity from IoTHub.
* @param {string} deviceId The name (Id) of the device.
* @returns void None
* @memberof IoTHubRegistryManager
*/
deleteDevice(
deviceId: string,
eTag: string = '*'
): void {
const devicesDeleteIdentityOptionalParams: DevicesDeleteIdentityOptionalParams = {
ifMatch: eTag
};
this._pl.devices.deleteIdentity(deviceId, devicesDeleteIdentityOptionalParams);
}
/**
* @method createModuleWithSas module: azure-iot-digitaltwins-service.IoTHubRegistryManager.createModuleWithSas
* @description Creates a module identity for a device on IoTHub using SAS authentication.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @param {string} managed_by The name of the manager device (edge).
* @param {string} primaryKey Primary authentication key.
* @param {string} secondaryKey Secondary authentication key.
* @returns Promise<ModulesCreateOrUpdateIdentityResponse> The return object containing the created module and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
createModuleWithSas(
deviceId: string,
moduleId: string,
managedBy: string,
primaryKey: string,
secondaryKey: string,
): Promise<ModulesCreateOrUpdateIdentityResponse> {
const symmetricKey: SymmetricKey = {
primaryKey: primaryKey,
secondaryKey: secondaryKey
};
const authenticationMechanism: AuthenticationMechanism = {
type: 'sas',
symmetricKey: symmetricKey
};
const module: Module = {};
module.deviceId = deviceId;
module.moduleId = moduleId;
module.managedBy = managedBy;
module.authentication = authenticationMechanism;
return this._pl.modules.createOrUpdateIdentity(deviceId, moduleId, module);
}
/**
* @method createModuleWithX509 module: azure-iot-digitaltwins-service.IoTHubRegistryManager.createModuleWithX509
* @description Creates a module identity for a device on IoTHub using X509 authentication.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @param {string} managed_by The name of the manager device (edge).
* @param {string} primaryThumbprint Primary X509 thumbprint.
* @param {string} secondaryThumbprint Secondary X509 thumbprint.
* @returns Promise<ModulesCreateOrUpdateIdentityResponse> The return object containing the created module and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
createModuleWithX509(
deviceId: string,
moduleId: string,
managedBy: string,
primaryThumbprint: string,
secondaryThumbprint: string,
): Promise<ModulesCreateOrUpdateIdentityResponse> {
const x509Thumbprint: X509Thumbprint = {
primaryThumbprint: primaryThumbprint,
secondaryThumbprint: secondaryThumbprint
};
const authenticationMechanism: AuthenticationMechanism = {
type: 'selfSigned',
x509Thumbprint: x509Thumbprint
};
const module: Module = {};
module.deviceId = deviceId;
module.moduleId = moduleId;
module.managedBy = managedBy;
module.authentication = authenticationMechanism;
return this._pl.modules.createOrUpdateIdentity(deviceId, moduleId, module);
}
/**
* @method createModuleWithCertificateAuthority module: azure-iot-digitaltwins-service.IoTHubRegistryManager.createModuleWithCertificateAuthority
* @description Creates a module identity for a device on IoTHub using certificate authority.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @param {string} managed_by The name of the manager device (edge).
* @returns Promise<ModulesCreateOrUpdateIdentityResponse> The return object containing the created module and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
createModuleWithCertificateAuthority(
deviceId: string,
moduleId: string,
managedBy: string,
): Promise<ModulesCreateOrUpdateIdentityResponse> {
const authenticationMechanism: AuthenticationMechanism = {
type: 'certificateAuthority'
};
const module: Module = {};
module.deviceId = deviceId;
module.moduleId = moduleId;
module.managedBy = managedBy;
module.authentication = authenticationMechanism;
return this._pl.modules.createOrUpdateIdentity(deviceId, moduleId, module);
}
/**
* @method updateModule module: azure-iot-digitaltwins-service.IoTHubRegistryManager.updateModule
* @description Updates a module identity for a device on IoTHub using SAS authentication.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @param {Module} module The contents of the module identity.
* @returns Promise<ModulesCreateOrUpdateIdentityOptionalParams> The return object containing the updated module and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
updateModule(
deviceId: string,
moduleId: string,
module: Module
): Promise<ModulesCreateOrUpdateIdentityResponse> {
const options: ModulesCreateOrUpdateIdentityOptionalParams = {
ifMatch: '*'
};
return this._pl.modules.createOrUpdateIdentity(deviceId, moduleId, module, options);
}
/**
* @method getModule module: azure-iot-digitaltwins-service.IoTHubRegistryManager.getModule
* @description Retrieves a module identity for a device from IoTHub.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @returns Promise<ModulesGetIdentityResponse> The return object containing the module and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
getModule(
deviceId: string,
moduleId: string
): Promise<ModulesGetIdentityResponse> {
return this._pl.modules.getIdentity(deviceId, moduleId);
}
/**
* @method getModules module: azure-iot-digitaltwins-service.IoTHubRegistryManager.getModules
* @description Retrieves all module identities on a device.
* @param {string} deviceId The name (Id) of the device.
* @returns Promise<ModulesGetModulesOnDeviceResponse> The return object containing the array of modules and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
getModules(
deviceId: string
): Promise<ModulesGetModulesOnDeviceResponse> {
return this._pl.modules.getModulesOnDevice(deviceId);
}
/**
* @method deleteModule module: azure-iot-digitaltwins-service.IoTHubRegistryManager.deleteModule
* @description Deletes a module identity for a device from IoTHub.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @returns void
* @memberof IoTHubRegistryManager
*/
deleteModule(
deviceId: string,
moduleId: string,
eTag: string = '*'
): void {
const modulesDeleteIdentityOptionalParams: ModulesDeleteIdentityOptionalParams = {
ifMatch: eTag
};
this._pl.modules.deleteIdentity(deviceId, moduleId, modulesDeleteIdentityOptionalParams);
}
/**
* @method getServiceStatistics module: azure-iot-digitaltwins-service.IoTHubRegistryManager.getServiceStatistics
* @description Retrieves the IoTHub service statistics.
* @returns Promise<StatisticsGetServiceStatisticsResponse> The return object containing the service statistics and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
getServiceStatistics(
): Promise<StatisticsGetServiceStatisticsResponse> {
return this._pl.statistics.getServiceStatistics();
}
/**
* @method getDeviceRegistryStatistics module: azure-iot-digitaltwins-service.IoTHubRegistryManager.getDeviceRegistryStatistics
* @description Retrieves the IoTHub device registry statistics.
* @returns Promise<StatisticsGetDeviceStatisticsResponse> The return object containing the registry statistics and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
getDeviceRegistryStatistics(
): Promise<StatisticsGetDeviceStatisticsResponse> {
return this._pl.statistics.getDeviceStatistics();
}
/**
* @method getDevices module: azure-iot-digitaltwins-service.IoTHubRegistryManager.getDevices
* @description Get the identities of multiple devices from the IoTHub identity
* registry. Not recommended. Use the IoTHub query language to retrieve
* device twin and device identity information. See
* https://docs.microsoft.com/en-us/rest/api/iothub/service/queryiothub
* and
* https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-query-language
* for more information.
* @param {number} maxNumberOfDevices This parameter when specified, defines the maximum number
* of device identities that are returned. Any value outside the range of
* 1-1000 is considered to be 1000.
* @returns Promise<DevicesGetDevicesResponse> The return object containing the array of devices and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
getDevices(
maxNumberOfDevices: number
): Promise<DevicesGetDevicesResponse> {
const devicesGetDevicesOptionalParams: DevicesGetDevicesOptionalParams = {
top: maxNumberOfDevices
};
return this._pl.devices.getDevices(devicesGetDevicesOptionalParams);
}
/**
* @method bulkCreateOrUpdateDevices module: azure-iot-digitaltwins-service.IoTHubRegistryManager.bulkCreateOrUpdateDevices
* @description Create, update, or delete the identities of multiple devices from the
* IoTHub identity registry.
* Create, update, or delete the identities of multiple devices from the
* IoTHub identity registry. A device identity can be specified only once
* in the list. Different operations (create, update, delete) on different
* devices are allowed. A maximum of 100 devices can be specified per
* invocation. For large scale operations, consider using the import
* feature using blob
* storage(https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-identity-registry#import-and-export-device-identities).
* @param {ExportImportDevice[]} devices The list of device objects to operate on.
* @returns Promise<BulkRegistryUpdateRegistryResponse> The return object containing the bulk update result and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
bulkCreateOrUpdateDevices(
devices: ExportImportDevice[]
): Promise<BulkRegistryUpdateRegistryResponse> {
return this._pl.bulkRegistry.updateRegistry(devices);
}
/**
* @method queryIoTHub module: azure-iot-digitaltwins-service.IoTHubRegistryManager.queryIoTHub
* @description Query an IoTHub to retrieve information regarding device twins using a
* SQL-like language.
* See https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-query-language
* for more information. Pagination of results is supported. This returns
* information about device twins only.
* @param {QuerySpecification} querySpecification The query specification.
* @returns Promise<QueryGetTwinsResponse> The return object containing the query result and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
queryIoTHub(
querySpecification: QuerySpecification
): Promise<QueryGetTwinsResponse> {
return this._pl.query.getTwins(querySpecification);
}
/**
* @method getTwin module: azure-iot-digitaltwins-service.IoTHubRegistryManager.getTwin
* @description Gets a device twin.
* @param {string} deviceId The name (Id) of the device.
* @returns Promise<DevicesGetTwinResponse> The return object containing the twin and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
getTwin(
deviceId: string
): Promise<DevicesGetTwinResponse> {
return this._pl.devices.getTwin(deviceId);
}
/**
* @method replaceTwin module: azure-iot-digitaltwins-service.IoTHubRegistryManager.replaceTwin
* @description Replaces tags and desired properties of a device twin.
* @param {string} deviceId The name (Id) of the device.
* @param {Twin} deviceTwin The twin info of the device.
* @returns Promise<DevicesReplaceTwinResponse> The return object containing the twin and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
replaceTwin(
deviceId: string,
deviceTwin: Twin
): Promise<DevicesReplaceTwinResponse> {
return this._pl.devices.replaceTwin(deviceId, deviceTwin);
}
/**
* @method updateTwin module: azure-iot-digitaltwins-service.IoTHubRegistryManager.updateTwin
* @description Updates tags and desired properties of a device twin.
* @param {string} deviceId The name (Id) of the device.
* @param {Twin} deviceTwin The twin info of the device.
* @param {string} eTag The etag (if_match) value to use for the update operation.
* @returns Promise<DevicesUpdateTwinResponse> The return object containing the twin and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
updateTwin(
deviceId: string,
deviceTwin: Twin,
eTag: string
): Promise<DevicesUpdateTwinResponse> {
const devicesUpdateTwinOptionalParams: DevicesUpdateTwinOptionalParams = {
ifMatch: eTag
};
return this._pl.devices.updateTwin(deviceId, deviceTwin, devicesUpdateTwinOptionalParams);
}
/**
* @method getModuleTwin module: azure-iot-digitaltwins-service.IoTHubRegistryManager.getModuleTwin
* @description Gets a module twin.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @returns Promise<ModulesGetTwinResponse> The return object containing the twin and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
getModuleTwin(
deviceId: string,
moduleId: string
): Promise<ModulesGetTwinResponse> {
return this._pl.modules.getTwin(deviceId, moduleId);
}
/**
* @method replaceModuleTwin module: azure-iot-digitaltwins-service.IoTHubRegistryManager.replaceModuleTwin
* @description Replaces tags and desired properties of a module twin.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @param {Twin} moduleTwin The twin info of the module.
* @returns Promise<ModulesReplaceTwinResponse> The return object containing the twin and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
replaceModuleTwin(
deviceId: string,
moduleId: string,
moduleTwin: Twin
): Promise<ModulesReplaceTwinResponse> {
return this._pl.modules.replaceTwin(deviceId, moduleId, moduleTwin);
}
/**
* @method updateModuleTwin module: azure-iot-digitaltwins-service.IoTHubRegistryManager.updateModuleTwin
* @description Updates tags and desired properties of a module twin.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @param {Twin} moduleTwin The twin info of the module.
* @param {string} eTag The etag (if_match) value to use for the update operation.
* @returns Promise<ModulesReplaceTwinResponse> The return object containing the twin and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
updateModuleTwin(
deviceId: string,
moduleId: string,
moduleTwin: Twin,
eTag: string
): Promise<ModulesReplaceTwinResponse> {
const modulesUpdateTwinOptionalParams: ModulesUpdateTwinOptionalParams = {
ifMatch: eTag
};
return this._pl.modules.updateTwin(deviceId, moduleId, moduleTwin, modulesUpdateTwinOptionalParams);
}
/**
* @method invokeDeviceMethod module: azure-iot-digitaltwins-service.IoTHubRegistryManager.invokeDeviceMethod
* @description Invoke a direct method on a device.
* @param {string} deviceId The name (Id) of the device.
* @param {CloudToDeviceMethod} directMethodRequest The method request.
* @returns Promise<DevicesInvokeMethodResponse> The return object containing the result of the method call and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
invokeDeviceMethod(
deviceId: string,
directMethodRequest: CloudToDeviceMethod
): Promise<DevicesInvokeMethodResponse> {
if (typeof directMethodRequest.payload !== 'undefined') {
directMethodRequest.payload = '';
}
return this._pl.devices.invokeMethod(deviceId, directMethodRequest);
}
/**
* @method invokeDeviceModuleMethod module: azure-iot-digitaltwins-service.IoTHubRegistryManager.invokeDeviceModuleMethod
* @description Invoke a direct method on a module of a device.
* @param {string} deviceId The name (Id) of the device.
* @param {string} moduleId The name (Id) of the module.
* @param {CloudToDeviceMethod} directMethodRequest The method request.
* @returns Promise<DevicesInvokeMethodResponse> The return object containing the result of the method call and the parsed HttpResponse.
* @memberof IoTHubRegistryManager
*/
invokeDeviceModuleMethod(
deviceId: string,
moduleId: string,
directMethodRequest: CloudToDeviceMethod
): Promise<DevicesInvokeMethodResponse> {
if (typeof directMethodRequest.payload !== 'undefined') {
directMethodRequest.payload = '';
}
return this._pl.modules.invokeMethod(deviceId, moduleId, directMethodRequest);
}
}

559
src/client.ts Normal file
Просмотреть файл

@ -0,0 +1,559 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { EventEmitter } from 'events';
import { Agent } from 'https';
import { anHourFromNow, errors, results, Message, Receiver, SharedAccessSignature } from 'azure-iot-common';
import { RetryOperation, RetryPolicy, ExponentialBackOffWithJitter } from 'azure-iot-common';
import * as ConnectionString from './connection_string';
import { Amqp } from './amqp';
import { DeviceMethod } from './device_method';
import { RestApiClient } from 'azure-iot-http-base';
import { DeviceMethodParams, IncomingMessageCallback, createResultWithIncomingMessage, ResultWithIncomingMessage } from './interfaces';
import { Callback, tripleValueCallbackToPromise } from 'azure-iot-common';
import { IncomingMessage } from 'http';
import { TokenCredential } from '@azure/core-http';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('../package.json');
const MAX_RETRY_TIMEOUT = 240000; // 4 minutes
/**
* The IoT Hub service client is used to communicate with devices through an Azure IoT hub.
* It lets the SDK user:
* - send cloud-to-device (also known as commands) to devices: commands are queued on IoT Hub and delivered asynchronously only when the device is connected. Only 50 commands can be queued per device.
* - invoke direct methods on devices (which will work only if the device is currently connected: it's a synchronous way of communicating with the device)
* - listen for feedback messages sent by devices for previous commands.
* - listen for file upload notifications from devices.
*
* Users should create new {@link azure-iothub.Client} instances by calling one of the factory methods,
* {@link azure-iothub.Client.fromConnectionString|fromConnectionString} or
* {@link azure-iothub.Client.fromSharedAccessSignature|fromSharedAccessSignature},
* to create an IoT Hub service Client.
*/
export class Client extends EventEmitter {
private _transport: Client.Transport;
private _restApiClient: RestApiClient;
private _retryPolicy: RetryPolicy;
/**
* @private
*/
constructor(transport: Client.Transport, restApiClient?: RestApiClient) {
super();
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_001: [The Client constructor shall throw ReferenceError if the transport argument is falsy.]*/
if (!transport) throw new ReferenceError('transport is \'' + transport + '\'');
this._transport = transport;
this._restApiClient = restApiClient;
if (this._restApiClient && this._restApiClient.setOptions) {
this._restApiClient.setOptions({ http: { agent: new Agent({ keepAlive: true }) } });
}
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_021: [The `Client` constructor shall initialize the default retry policy to `ExponentialBackoffWithJitter` with a maximum timeout of 4 minutes.]*/
this._retryPolicy = new ExponentialBackOffWithJitter();
}
/**
* @method module:azure-iothub.Client#open
* @description Opens the connection to an IoT hub.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be passed an Error object
* argument, which will be null if the operation
* completed successfully.
* @returns {Promise<ResultWithIncomingMessage<results.Connected>> | void} Promise if no callback function was passed, void otherwise.
*/
open(done: IncomingMessageCallback<results.Connected>): void;
open(): Promise<ResultWithIncomingMessage<results.Connected>>;
open(done?: IncomingMessageCallback<results.Connected>): Promise<ResultWithIncomingMessage<results.Connected>> | void {
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_008: [The open method shall open a connection to the IoT Hub that was identified when the Client object was created (e.g., in Client.fromConnectionString).]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_009: [When the open method completes, the callback function (indicated by the done argument) shall be invoked with the following arguments:
err - standard JavaScript Error object (or subclass)]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_010: [The argument err passed to the callback done shall be null if the protocol operation was successful.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_011: [Otherwise the argument err shall have a transport property containing implementation-specific response information for use in logging and troubleshooting.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_012: [If the connection is already open when open is called, it shall have no effect—that is, the done callback shall be invoked immediately with a null argument.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_006: [The `open` method should not throw if the `done` callback is not specified.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_022: [The `open` method shall use the retry policy defined either by default or by a call to `setRetryPolicy` if necessary to connect the transport.]*/
const retryOp = new RetryOperation('open', this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry((retryCallback) => {
this._transport.connect(retryCallback);
},
(err, result) => {
if (err) {
if (_callback) _callback(err);
} else {
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_002: [If the transport successfully establishes a connection the `open` method shall subscribe to the `disconnect` event of the transport.]*/
this._transport.on('disconnect', this._disconnectHandler.bind(this));
if (_callback) _callback(null, result);
}
});
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, done);
}
/**
* @method module:azure-iothub.Client#close
* @description Closes the connection to an IoT hub.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be passed an Error object
* argument, which will be null if the operation
* completed successfully.
* @returns {Promise<ResultWithIncomingMessage<results.Disconnected>> | void} Promise if no callback function was passed, void otherwise.
*/
close(done: IncomingMessageCallback<results.Disconnected>): void;
close(): Promise<ResultWithIncomingMessage<results.Disconnected>>;
close(done?: IncomingMessageCallback<results.Disconnected>): Promise<ResultWithIncomingMessage<results.Disconnected>> | void {
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_021: [The close method shall close the connection.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_022: [When the close method completes, the callback function (indicated by the done argument) shall be invoked with the following arguments:
err - standard JavaScript Error object (or subclass)]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_023: [The argument err passed to the callback _callback shall be null if the protocol operation was successful.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_024: [Otherwise the argument err shall have a transport property containing implementation-specific response information for use in logging and troubleshooting.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_025: [If the connection is not open when close is called, it shall have no effect— that is, the _callback callback shall be invoked immediately with null arguments.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_005: [The `close` method should not throw if the `_callback` callback is not specified.]*/
this._transport.disconnect((err, result) => {
if (err) {
if (_callback) _callback(err);
} else {
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_003: [The `close` method shall remove the listener that has been attached to the transport `disconnect` event.]*/
this._transport.removeAllListeners('disconnect');
if (_callback) _callback(null, result);
}
});
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, done);
}
/**
* @method module:azure-iothub.Client#send
* @description Sends a message to a device.
* @param {String} deviceId The identifier of an existing device identity.
* @param {Object} message The body of the message to send to the device.
* If `message` is not of type
* {@link module:azure-iot-common.Message|Message},
* it will be converted.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be called with two
* arguments: an Error object (can be null) and a
* transport-specific response object useful for
* logging or debugging.
* @returns {Promise<ResultWithIncomingMessage<results.MessageEnqueued>> | void} Promise if no callback function was passed, void otherwise.
*
* @throws {ReferenceError} If `deviceId` or `message` is null, undefined or empty.
*/
send(deviceId: string, message: Message | Message.BufferConvertible, done: IncomingMessageCallback<results.MessageEnqueued>): void;
send(deviceId: string, message: Message | Message.BufferConvertible): Promise<ResultWithIncomingMessage<results.MessageEnqueued>>;
send(deviceId: string, message: Message | Message.BufferConvertible, done?: IncomingMessageCallback<results.MessageEnqueued>): Promise<ResultWithIncomingMessage<results.MessageEnqueued>> | void {
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_013: [The send method shall throw ReferenceError if the deviceId or message arguments are falsy.]*/
if (!deviceId) {
throw new ReferenceError('deviceId is \'' + deviceId + '\'');
}
if (!message) {
throw new ReferenceError('message is \'' + message + '\'');
}
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_014: [The `send` method shall convert the `message` object to type `azure-iot-common.Message` if it is not already of type `azure-iot-common.Message`.]*/
if ((<any>message.constructor).name !== 'Message') {
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_016: [The `send` method shall throw an `ArgumentError` if the `message` argument is not of type `azure-iot-common.Message` or `azure-iot-common.Message.BufferConvertible`.]*/
if (!Message.isBufferConvertible(message)) {
throw new errors.ArgumentError('message is not of type Message or Message.BufferConvertible');
}
message = new Message(message as Message.BufferConvertible);
}
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_015: [If the connection has not already been opened (e.g., by a call to open), the send method shall open the connection before attempting to send the message.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_016: [When the send method completes, the callback function (indicated by the _callback argument) shall be invoked with the following arguments:
err - standard JavaScript Error object (or subclass)
response - an implementation-specific response object returned by the underlying protocol, useful for logging and troubleshooting]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_017: [The argument err passed to the callback _callback shall be null if the protocol operation was successful.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_018: [Otherwise the argument err shall have a transport property containing implementation-specific response information for use in logging and troubleshooting.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_019: [If the deviceId has not been registered with the IoT Hub, send shall return an instance of DeviceNotFoundError.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_020: [If the queue which receives messages on behalf of the device is full, send shall return and instance of DeviceMaximumQueueDepthExceededError.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_023: [The `send` method shall use the retry policy defined either by default or by a call to `setRetryPolicy` if necessary to send the message.]*/
const retryOp = new RetryOperation('send' , this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry((retryCallback) => {
this._transport.send(deviceId, message as Message, retryCallback);
}, (err, result) => {
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_030: [The `send` method shall not throw if the `_callback` callback is falsy.]*/
if (_callback) {
if (err) {
_callback(err);
} else {
_callback(null, result);
}
}
});
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, done);
}
_invokeDeviceMethod(deviceId: string, moduleIdOrMethodParams: string | DeviceMethodParams, methodParamsOrDone?: DeviceMethodParams | IncomingMessageCallback<any>, done?: IncomingMessageCallback<any>): void {
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_014: [The `invokeDeviceMethod` method shall throw a `ReferenceError` if `deviceId` is `null`, `undefined` or an empty string.]*/
if (deviceId === undefined || deviceId === null || deviceId === '') throw new ReferenceError('deviceId cannot be \'' + deviceId + '\'');
let actualModuleId: string = undefined;
let actualMethodParams: DeviceMethodParams = undefined;
let actualCallback: IncomingMessageCallback<any> | undefined = undefined;
if (typeof moduleIdOrMethodParams === 'string') {
actualModuleId = moduleIdOrMethodParams;
actualMethodParams = methodParamsOrDone as DeviceMethodParams;
actualCallback = done;
} else {
// actualModuleId stays undefined
actualMethodParams = moduleIdOrMethodParams;
actualCallback = methodParamsOrDone as IncomingMessageCallback<any>;
}
// Validation of the validity of actualMethodParams is handled in the DeviceMethod constructor.
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_009: [The `invokeDeviceMethod` method shall initialize a new `DeviceMethod` instance with the `methodName`, `payload` and `timeout` values passed in the arguments.]*/
const method = new DeviceMethod(actualMethodParams, this._restApiClient);
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_010: [The `invokeDeviceMethod` method shall use the newly created instance of `DeviceMethod` to invoke the method on the device specified with the `deviceid` argument .]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_012: [The `invokeDeviceMethod` method shall call the `done` callback with a standard javascript `Error` object if the request failed.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_013: [The `invokeDeviceMethod` method shall call the `done` callback with a `null` first argument, the result of the method execution in the second argument, and the transport-specific response object as a third argument.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_026: [The `invokeDeviceMethod` method shall use the retry policy defined either by default or by a call to `setRetryPolicy` if necessary to send the method request.]*/
const retryOp = new RetryOperation('invokeOnModule', this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry((retryCallback) => {
/*Codes_SRS_NODE_IOTHUB_CLIENT_18_003: [If `moduleIdOrMethodParams` is a string the `invokeDeviceMethod` method shall call `invokeOnModule` on the new `DeviceMethod` instance. ]*/
if (actualModuleId) {
method.invokeOnModule(deviceId, actualModuleId, retryCallback);
} else {
method.invokeOn(deviceId, retryCallback);
}
}, (err, result, response) => {
if (actualCallback) {
if (err) {
actualCallback(err);
} else {
actualCallback(null, result, response);
}
}
});
}
/**
* @method module:azure-iothub.Client#invokeDeviceMethod
* @description Invokes a method on a particular device or module.
* @param {String} deviceId The identifier of an existing device identity.
* @param {String} moduleId The identifier of an existing module identity (optional)
* @param {Object} params An object describing the method and shall have the following properties:
* - methodName The name of the method that shall be invoked.
* - payload [optional] The payload to use for the method call.
* - responseTimeoutInSeconds [optional] The number of seconds IoT Hub shall wait for the device
* to send a response before deeming the method execution a failure.
* - connectTimeoutInSeconds [optional] The number of seconds IoT Hub shall wait for the service
* to connect to the device before declaring the device is unreachable.
* @param {Function} [done] The optional callback to call with the result of the method execution.
* @returns {ResultWithIncomingMessage<any> | void} Promise if no callback function was passed, void otherwise.
*
* @throws {ReferenceError} If one of the required parameters is null, undefined or empty.
* @throws {TypeError} If one of the parameters is of the wrong type.
*/
invokeDeviceMethod(deviceId: string, methodParams: DeviceMethodParams, done: IncomingMessageCallback<any>): void;
invokeDeviceMethod(deviceId: string, moduleId: string, methodParams: DeviceMethodParams, done: IncomingMessageCallback<any>): void;
invokeDeviceMethod(deviceId: string, methodParams: DeviceMethodParams): Promise<ResultWithIncomingMessage<any>>;
invokeDeviceMethod(deviceId: string, moduleId: string, methodParams: DeviceMethodParams): Promise<ResultWithIncomingMessage<any>>;
invokeDeviceMethod(deviceId: string, moduleIdOrMethodParams: string | DeviceMethodParams, methodParamsOrDone?: DeviceMethodParams | IncomingMessageCallback<any>, done?: IncomingMessageCallback<any>): Promise<ResultWithIncomingMessage<any>> | void {
const callback = done || ((typeof methodParamsOrDone === 'function') ? methodParamsOrDone as IncomingMessageCallback<any> : undefined);
let moduleId: string;
let methodParams: DeviceMethodParams;
if (callback) {
return this._invokeDeviceMethod(deviceId, moduleIdOrMethodParams, methodParamsOrDone, done);
} else {
if (typeof moduleIdOrMethodParams === 'string') {
moduleId = moduleIdOrMethodParams;
if (methodParamsOrDone) {
methodParams = methodParamsOrDone as DeviceMethodParams;
}
} else {
moduleId = undefined;
methodParams = moduleIdOrMethodParams as DeviceMethodParams;
}
}
if (moduleId) {
return tripleValueCallbackToPromise((_callback) => {
this._invokeDeviceMethod(deviceId, moduleId, methodParams, _callback);
}, (r: any, m: IncomingMessage) => {
return createResultWithIncomingMessage(r, m);
}, callback);
} else {
return tripleValueCallbackToPromise((_callback) => {
this._invokeDeviceMethod(deviceId, methodParams, _callback);
}, (r: any, m: IncomingMessage) => {
return createResultWithIncomingMessage(r, m);
}, callback);
}
}
/**
* @method module:azure-iothub.Client#getFeedbackReceiver
* @description Returns a AmqpReceiver object which emits events when new feedback messages are received by the client.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be called with two
* arguments: an Error object (can be null) and a
* AmqpReceiver object.
* @returns {ResultWithIncomingMessage<Client.ServiceReceiver> | void} Promise if no callback function was passed, void otherwise.
*/
getFeedbackReceiver(done: IncomingMessageCallback<Client.ServiceReceiver>): void;
getFeedbackReceiver(): Promise<ResultWithIncomingMessage<Client.ServiceReceiver>>;
getFeedbackReceiver(done?: IncomingMessageCallback<Client.ServiceReceiver>): Promise<ResultWithIncomingMessage<Client.ServiceReceiver>> | void {
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_027: [When the `getFeedbackReceiver` method completes, the callback function (indicated by the `done` argument) shall be invoked with the following arguments:
- `err` - standard JavaScript `Error` object (or subclass): `null` if the operation was successful
- `receiver` - an `AmqpReceiver` instance: `undefined` if the operation failed]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_030: [The FeedbackReceiver class shall inherit EventEmitter to provide consumers the ability to listen for (and stop listening for) events.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_031: [FeedbackReceiver shall expose the 'errorReceived' event, whose handler shall be called with the following arguments:
err standard JavaScript Error object (or subclass)]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_032: [FeedbackReceiver shall expose the 'message' event, whose handler shall be called with the following arguments when a new feedback message is received from the IoT Hub:
message a JavaScript object containing a batch of one or more feedback records]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_033: [getFeedbackReceiver shall return the same instance of Client.FeedbackReceiver every time it is called with a given instance of Client.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_024: [The `getFeedbackReceiver` method shall use the retry policy defined either by default or by a call to `setRetryPolicy` if necessary to get a feedback receiver object.]*/
const retryOp = new RetryOperation('getFeedbackReceiver', this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry((retryCallback) => {
this._transport.getFeedbackReceiver(retryCallback);
}, (err, result) => {
if (_callback) {
if (err) {
_callback(err);
} else {
_callback(null, result);
}
}
});
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, done);
}
/**
* @method module:azure-iothub.Client#getFileNotificationReceiver
* @description Returns a AmqpReceiver object which emits events when new file upload notifications are received by the client.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be called with two
* arguments: an Error object (can be null) and a
* AmqpReceiver object.
* @returns {ResultWithIncomingMessage<Client.ServiceReceiver> | void} Promise if no callback function was passed, void otherwise.
*/
getFileNotificationReceiver(done: IncomingMessageCallback<Client.ServiceReceiver>): void;
getFileNotificationReceiver(): Promise<ResultWithIncomingMessage<Client.ServiceReceiver>>;
getFileNotificationReceiver(done?: IncomingMessageCallback<Client.ServiceReceiver>): Promise<ResultWithIncomingMessage<Client.ServiceReceiver>> | void {
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_001: [When the `getFileNotificationReceiver` method completes, the callback function (indicated by the `done` argument) shall be invoked with the following arguments:
- `err` - standard JavaScript `Error` object (or subclass): `null` if the operation was successful
- `receiver` - an `AmqpReceiver` instance: `undefined` if the operation failed]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_025: [The `getFileNotificationReceiver` method shall use the retry policy defined either by default or by a call to `setRetryPolicy` if necessary to send the get a feedback receiver object.]*/
const retryOp = new RetryOperation('getFileNotificationReceiver', this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry((retryCallback) => {
this._transport.getFileNotificationReceiver(retryCallback);
}, (err, result) => {
if (_callback) {
if (err) {
_callback(err);
} else {
_callback(null, result);
}
}
});
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, done);
}
/**
* Set the policy used by the client to retry network operations.
*
* @param policy policy used to retry operations (eg. open, send, etc.).
* The SDK comes with 2 "built-in" policies: ExponentialBackoffWithJitter (default)
* and NoRetry (to cancel any form of retry). The user can also pass its own object as
* long as it implements 2 methods:
* - shouldRetry(err: Error): boolean : indicates whether an operation should be retried based on the error type
* - nextRetryTimeout(retryCount: number, throttled: boolean): number : returns the time to wait (in milliseconds)
* before retrying based on the past number of attempts (retryCount) and the fact that the error is a throttling error or not.
*/
setRetryPolicy(policy: RetryPolicy): void {
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_027: [The `setRetryPolicy` method shall throw a `ReferenceError` if the `policy` argument is falsy.]*/
if (!policy) {
throw new ReferenceError('policy cannot be \'' + policy + '\'');
}
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_028: [The `setRetryPolicy` method shall throw an `ArgumentError` if the `policy` object does not have a `shouldRetry` method and a `nextRetryTimeout` method.]*/
if (!(typeof policy.shouldRetry === 'function') || !(typeof policy.nextRetryTimeout === 'function')) {
throw new errors.ArgumentError('policy should have a shouldRetry method and a nextRetryTimeout method');
}
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_029: [Any operation (e.g. `send`, `getFeedbackReceiver`, etc) initiated after a call to `setRetryPolicy` shall use the policy passed as argument to retry.]*/
this._retryPolicy = policy;
}
private _disconnectHandler(reason: string): void {
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_004: [** The `disconnect` event shall be emitted when the client is disconnected from the server.]*/
const evt = new results.Disconnected();
evt.reason = reason;
this.emit('disconnect', evt);
}
/**
* @method module:azure-iothub.Client.fromConnectionString
* @static
* @description Creates an IoT Hub service client from the given
* connection string using the default transport
* (Amqp) or the one specified in the second argument.
*
* @param {String} connStr A connection string which encapsulates "device
* connect" permissions on an IoT hub.
* @param {Function} Transport A transport constructor.
*
* @returns {module:azure-iothub.Client}
*/
static fromConnectionString(connStr: string, transportCtor?: Client.TransportCtor): Client {
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_002: [The fromConnectionString method shall throw ReferenceError if the connStr argument is falsy.]*/
if (!connStr) throw new ReferenceError('connStr is \'' + connStr + '\'');
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_016: [The `fromConnectionString` method shall use the `Transport` constructor passed as argument to instantiate a transport object if it's not falsy.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_017: [The `fromConnectionString` method shall use the default Transport (Amqp) if the `Transport` optional argument is falsy.]*/
if (!transportCtor) {
transportCtor = Amqp;
}
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_015: [The `fromConnectionString` method shall create a new transport instance and pass it a config object formed from the connection string given as argument.]*/
const cn = ConnectionString.parse(connStr);
const config: Client.TransportConfigOptions = {
host: cn.HostName,
keyName: cn.SharedAccessKeyName,
sharedAccessSignature: SharedAccessSignature.create(cn.HostName, cn.SharedAccessKeyName, cn.SharedAccessKey, anHourFromNow()),
tokenCredential: undefined
};
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_004: [The fromConnectionString method shall return a new instance of the Client object, as by a call to new Client(transport).]*/
return new Client(new transportCtor(config), new RestApiClient(config, packageJson.name + '/' + packageJson.version));
}
/**
* @method module:azure-iothub.Client.fromSharedAccessSignature
* @static
* @description Creates an IoT Hub service client from the given
* shared access signature using the default transport
* (Amqp) or the one specified in the second argument.
*
* @param {String} sharedAccessSignature A shared access signature which encapsulates
* "service connect" permissions on an IoT hub.
* @param {Function} Transport A transport constructor.
*
* @returns {module:azure-iothub.Client}
*/
static fromSharedAccessSignature(sharedAccessSignature: string, transportCtor?: Client.TransportCtor): Client {
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_005: [The fromSharedAccessSignature method shall throw ReferenceError if the sharedAccessSignature argument is falsy.]*/
if (!sharedAccessSignature) throw new ReferenceError('sharedAccessSignature is \'' + sharedAccessSignature + '\'');
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_019: [The `fromSharedAccessSignature` method shall use the `Transport` constructor passed as argument to instantiate a transport object if it's not falsy.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_020: [The `fromSharedAccessSignature` method shall use the default Transport (Amqp) if the `Transport` optional argument is falsy.]*/
if (!transportCtor) {
transportCtor = Amqp;
}
const sas = SharedAccessSignature.parse(sharedAccessSignature);
const decodedUri = decodeURIComponent(sas.sr);
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_018: [The `fromSharedAccessSignature` method shall create a new transport instance and pass it a config object formed from the connection string given as argument.]*/
const config: Client.TransportConfigOptions = {
host: decodedUri,
keyName: sas.skn,
sharedAccessSignature: sas.toString(),
tokenCredential: undefined
};
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_007: [The fromSharedAccessSignature method shall return a new instance of the Client object, as by a call to new Client(transport).]*/
return new Client(new transportCtor(config), new RestApiClient(config, packageJson.name + '/' + packageJson.version));
}
/**
* @method module:azure-iothub.Client.fromTokenCredential
* @description Creates an IoT Hub service client from the given
* Azure tokenCredential using the default transport
* (Amqp) or the one specified in the second argument.
* @static
*
* @param {String} hostName Host name of the Azure service.
* @param {String} tokenCredential An Azure TokenCredential used to authenticate
* with the Azure service
* @param {Function} Transport A transport constructor.
*
* @throws {ReferenceError} If the tokenCredential argument is falsy.
*
* @returns {module:azure-iothub.Client}
*/
static fromTokenCredential(hostName: string, tokenCredential: TokenCredential, transportCtor?: Client.TransportCtor): Client {
if (!transportCtor) {
transportCtor = Amqp;
}
const config: Client.TransportConfigOptions = {
host: hostName,
keyName: '',
sharedAccessSignature: undefined,
tokenCredential,
tokenScope: 'https://iothubs.azure.net/.default'
};
return new Client(new transportCtor(config), new RestApiClient(config, packageJson.name + '/' + packageJson.version));
}
}
// eslint-disable-next-line no-redeclare
export namespace Client {
export interface TransportConfigOptions {
/**
* Hostname of the Azure IoT hub. (<IoT hub name>.azure-devices.net).
*/
host: string;
/**
* @deprecated This is not used anywhere anymore.
* Name of the Azure IoT hub. (The first section of the Azure IoT hub hostname)
*/
hubName?: string;
/**
* The name of the policy used to connect to the Azure IoT Hub service.
*/
keyName?: string;
/**
* The shared access signature token used to authenticate the connection with the Azure IoT hub.
*/
sharedAccessSignature?: string | SharedAccessSignature;
/**
* The token credential used to authenticate the connection with the Azure IoT hub.
*/
tokenCredential?: TokenCredential;
/**
* The token scope used to get the token from the TokenCredential object
*/
tokenScope?: string;
}
export interface ServiceReceiver extends Receiver {
complete(message: Message, done?: Callback<results.MessageCompleted>): void;
abandon(message: Message, done?: Callback<results.MessageAbandoned>): void;
reject(message: Message, done?: Callback<results.MessageRejected>): void;
}
export interface Transport extends EventEmitter {
connect(done?: Callback<results.Connected>): void;
disconnect(done: Callback<results.Disconnected>): void;
send(deviceId: string, message: Message, done?: Callback<results.MessageEnqueued>): void;
getFeedbackReceiver(done: Callback<ServiceReceiver>): void;
getFileNotificationReceiver(done: Callback<ServiceReceiver>): void;
}
export type TransportCtor = new (config: Client.TransportConfigOptions) => Client.Transport;
}

99
src/configuration.ts Normal file
Просмотреть файл

@ -0,0 +1,99 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
/**
* The Configuration object represents the configuration of a single device or it can represent a deployment which can be applied as a configuration to devices based on the targetCondition.
*/
export interface Configuration {
/**
* Id of this configuration.
*/
id: string;
/**
* Version of the schema.
*/
schemaVersion: string;
/**
* This is a dictionary<string, string> of labels.
* Labels are a set of case-sensitive string key value pairs that you can use to describe a deployment.
* Both keys and values are case-sensitive strings (up to 128 char long) of ASCII 7-bit alphanumeric chars + {'-', ':', '.', '+', '%', '_', '#', '*', '?', '!', '(', ')', ',', '=', '@', ';', '''}
* (Note that $ is reserved)
*/
labels?: {[key: string]: string};
/**
* Content of the configuration
*/
content?: ConfigurationContent;
/**
* The target condition is continuously evaluated to include any new devices that meet the requirements or remove devices that no longer do through the life time of the deployment.
* Use any Boolean condition on device twins tags or deviceId to select the target devices, e.g. tags.environment='prod' or deviceId='linuxprod' or tags.environment = 'prod' AND tags.location = 'westus'.
*/
targetCondition?: string;
/**
* @readonly
* Date time in ISO6801 of the creation of this configuration
*/
createdTimeUtc?: string;
/**
* @readonly
* Date time in ISO6801 of the last update of this configuration
*/
lastUpdatedTimeUtc?: string;
/**
* When two deployments target the same device, the deployment with higher priority gets applied. If two deployments have the same priority, the deployment with the later creation date gets applied.
*/
priority?: number;
/**
* System Configuration Metrics
*/
systemMetrics?: ConfigurationMetrics;
/**
* Custom Configuration Metrics
*/
metrics?: ConfigurationMetrics;
/**
* A string used for protecting opportunistic concurrency updates by the caller. This gets updated when deployment is update
*/
etag?: string;
}
export interface ConfigurationMetrics {
/**
* @readonly
* Results of the metrics collection queries
*/
results?: {[key: string]: number};
/**
* Queries used for metrics collection
*/
queries?: {[key: string]: string};
}
export interface ConfigurationContent {
/**
* The configuration for edge modules.
*/
modulesContent?: {[key: string]: unknown};
/**
* The configuration for device modules
*/
moduleContent?: {[key: string]: unknown};
/**
* The configuration for all the devices.
*/
deviceContent?: {[key: string]: unknown};
}

20
src/connection_string.ts Normal file
Просмотреть файл

@ -0,0 +1,20 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { ConnectionString } from 'azure-iot-common';
/**
* Parses a connection string from a string.
* See {@link https://blogs.msdn.microsoft.com/iotdev/2017/05/09/understand-different-connection-strings-in-azure-iot-hub/|Understanding Connection Strings in Azure IoT Hub} for more details.
*
* @param source the string from which the {@link ConnectionString} object should be parsed.
*
* @throws {azure-iot-common.ArgumentError} if the string is missing one of the required attributes.
*/
export function parse(source: string): ConnectionString {
/*Codes_SRS_NODE_IOTHUB_CONNSTR_05_001: [The parse method shall return the result of calling azure-iot-common.ConnectionString.parse.]*/
/*Codes_SRS_NODE_IOTHUB_CONNSTR_05_002: [It shall throw ArgumentError if any of 'HostName', 'SharedAccessKeyName', or 'SharedAccessKey' fields are not found in the source argument.]*/
return ConnectionString.parse(source, ['HostName', 'SharedAccessKeyName', 'SharedAccessKey']);
}

170
src/device.ts Normal file
Просмотреть файл

@ -0,0 +1,170 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import * as _ from 'lodash';
/**
* @private
*/
export interface DeviceIdentity {
deviceId: string;
generationId?: string;
etag?: string;
connectionState?: Device.ConnectionState;
status?: Device.DeviceStatus;
statusReason?: string;
connectionStateUpdatedTime?: string;
statusUpdatedTime?: string;
lastActivityTime?: string;
cloudToDeviceMessageCount?: string;
authentication?: Device.Authentication;
capabilities?: Device.Capabilities;
deviceScope?: string;
parentScopes?: [string];
}
/**
* Creates a representation of a device for use with the [device identity registry]{@link azure-iothub.Registry} APIs.
*
* **This class is deprecated** because the [device identity registry]{@link azure-iothub.Registry} can work directly with JSON objects
* containing a partial description of the device, not necessarily the full object. On top of that initially this class was shipped with a typo
* on the `symmetricKey` property name (it was pascal-cased instead of camel-cased). The SDK is keeping this class around in order not to break existing code
* but this will be removed in a future major version update and customers should instead use plain JSON objects.
*
* @deprecated
*/
/*Codes_SRS_NODE_SERVICE_DEVICE_16_001: [The constructor shall accept a `null` or `undefined` value as argument and create an empty `Device` object.]*/
export class Device implements DeviceIdentity {
/**
* Unique device identifier as it exists in the Azure IoT hub device registry.
*/
deviceId: string;
/**
* Used to disambiguate devices that have been deleted/recreated with the same `deviceId`
*/
generationId?: string;
/**
* Weak entity tag assigned to this device identity description
*/
etag?: string;
/**
* Whether the device is 'connected' or 'disconnected'. It is not recommended to use this property to determine if the device is actually connected right now though,
* since the device connection may have timed out and the IoT hub may not have detected it, or if the device is using HTTPS to connect.
* If you have a need to monitor device connections, the recommended way is to use the [operations monitoring]{@link https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-operations-monitoring} feature of your hub.
*/
connectionState?: Device.ConnectionState;
/**
* 'enabled' (device authorized to connect, can send/receive messages) or 'disabled'.
*/
status?: Device.DeviceStatus;
/**
* 128-character string set when the device is disabled.
*/
statusReason?: string;
/**
* Timestamp representing the last time `connectionState` changed.
*/
connectionStateUpdatedTime?: string;
/**
* Timestamp representing the last time `status` changed.
*/
statusUpdatedTime?: string;
/**
* Timestamp representing the last time the device authenticated, sent a message, or received a message.
*/
lastActivityTime?: string;
/**
* Number of c2d messages waiting to by delivered to the device.
*/
cloudToDeviceMessageCount?: string;
/**
* Contains the symmetric keys used to authenticate this device.
*/
authentication?: Device.Authentication;
/**
* Contains the capabilities of this device.
*/
capabilities?: Device.Capabilities;
/**
* Instantiate a new {@link azure-iothub.Device} object.
*
* @param jsonData An optional JSON representation of the device, which will be mapped to properties in the object. If no argument is provided, Device properties will be assigned default values.
*/
constructor(jsonData?: any) {
this.deviceId = null;
this.generationId = null;
this.etag = null;
this.connectionState = 'disconnected';
this.status = 'enabled';
this.statusReason = null;
this.connectionStateUpdatedTime = null;
this.statusUpdatedTime = null;
this.lastActivityTime = null;
this.cloudToDeviceMessageCount = '0';
this.capabilities = {};
this.authentication = {
symmetricKey: { // <- this is the correct thing (camel-cased)
primaryKey: null,
secondaryKey: null
},
x509Thumbprint: {
primaryThumbprint: null,
secondaryThumbprint: null
}
};
/*Codes_SRS_NODE_SERVICE_DEVICE_16_002: [If the `deviceDescription` argument is provided as a string, it shall be parsed as JSON and the properties of the new `Device` object shall be populated with the values provided in the `deviceDescription` JSON string.]*/
/*Codes_SRS_NODE_SERVICE_DEVICE_16_003: [If the `deviceDescription` argument if provided as an object, the properties of the new `Device` object shall be populated with the values provided in the `deviceDescription` JSON string.]*/
if (jsonData) {
const userProps = (typeof jsonData === 'string') ? JSON.parse(jsonData) : jsonData;
if (!userProps.deviceId) {
/*Codes_SRS_NODE_SERVICE_DEVICE_16_004: [The constructor shall throw a `ReferenceError` if the `deviceDescription` argument doesn't contain a `deviceId` property.]*/
throw new ReferenceError('The \'deviceId\' property cannot be \'' + userProps.deviceId + '\'');
}
_.merge(this, userProps);
}
Object.defineProperty(this.authentication, 'SymmetricKey', {
enumerable: true,
get: function (): Device._SymmetricKey {
/*Codes_SRS_NODE_SERVICE_DEVICE_16_005: [The `authentication.SymmetricKey` property shall return the content of the `authentication.symmetricKey` property (the latter being the valid property returned by the IoT hub in the device description).]*/
return this.symmetricKey;
}
});
}
}
// eslint-disable-next-line no-redeclare
export namespace Device {
// eslint-disable-next-line @typescript-eslint/naming-convention
export interface _SymmetricKey {
primaryKey: string;
secondaryKey: string;
}
export interface Authentication {
// eslint-disable-next-line @typescript-eslint/naming-convention
SymmetricKey?: _SymmetricKey;
symmetricKey?: _SymmetricKey;
x509Thumbprint?: X509Thumbprints;
}
export interface X509Thumbprints {
primaryThumbprint?: string;
secondaryThumbprint?: string;
}
export interface Capabilities {
iotEdge?: boolean;
[x: string]: any;
}
export type ConnectionState = 'connected' | 'disconnected';
export type DeviceStatus = 'enabled' | 'disabled';
}

148
src/device_method.ts Normal file
Просмотреть файл

@ -0,0 +1,148 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { RestApiClient } from 'azure-iot-http-base';
import { DeviceMethodParams } from './interfaces';
import { TripleValueCallback, tripleValueCallbackToPromise } from 'azure-iot-common';
import { versionQueryString } from './version';
/**
* @private
* @class module:azure-iothub.DeviceMethod
* @classdesc Constructs a DeviceMethod object that provides APIs to trigger the execution of a device method.
* @param {Object} params An object describing the method and shall have the following properties:
* - methodName The name of the method that shall be invoked.
* - payload [optional] The payload to use for the method call.
* - timeoutInSeconds [optional] The number of seconds IoT Hub shall wait for the device
* to send a response before deeming the method execution a failure.
* @param {RestApiClient} restApiClient The REST client used to execute API calls.
*/
export class DeviceMethod {
static defaultResponseTimeout: number = 30;
static defaultConnectTimeout: number = 0;
static defaultPayload: any = null;
params: DeviceMethodParams;
private _client: RestApiClient;
constructor(params: DeviceMethodParams, restApiClient: RestApiClient) {
if (!params) throw new ReferenceError('params cannot be \'' + params + '\'');
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_004: [The `DeviceMethod` constructor shall throw a `ReferenceError` if `methodName` is `null`, `undefined` or an empty string.]*/
if (params.methodName === null || params.methodName === undefined || params.methodName === '') throw new ReferenceError('params.methodName cannot be \'' + params.methodName + '\'');
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_005: [The `DeviceMethod` constructor shall throw a `TypeError` if `methodName` is not a `string`.]*/
if (typeof params.methodName !== 'string') throw new TypeError('methodName must be a string');
this._client = restApiClient;
this.params = params;
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_006: [The `DeviceMethod` constructor shall set the `DeviceMethod.params.responseTimeoutInSeconds` property value to the `responseTimeoutInSeconds` argument value or to the default (`30`) if the `responseTimeoutInSeconds` value is falsy.]*/
this.params.responseTimeoutInSeconds = this.params.responseTimeoutInSeconds || DeviceMethod.defaultResponseTimeout;
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_016: [** The `DeviceMethod` constructor shall set the `DeviceMethod.params.connectTimeoutInSeconds` property value to the `params.connectTimeoutInSeconds` argument value or to the default (`0`) if the `connectTimeoutInSeconds` value is falsy.]*/
this.params.connectTimeoutInSeconds = this.params.connectTimeoutInSeconds || DeviceMethod.defaultConnectTimeout;
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_015: [The `DeviceMethod` constructor shall set the `DeviceMethod.params.payload` property value to the `payload` argument value or to the default (`null`) if the `payload` argument is `null` or `undefined`.]*/
this.params.payload = (this.params.payload === undefined || this.params.payload === null) ? DeviceMethod.defaultPayload : this.params.payload;
}
/**
* @method module:azure-iothub.DeviceMethod.invokeOn
* @description Invokes the method on the specified device with the specified payload.
* @param {String} deviceId Identifier of the device on which the method will run.
* @param {Function} [done] The optional function to call when the operation is
* complete. done` will be called with three
* arguments: an Error object (can be null), the
* body of the response, and a transport-specific
* response object useful for logging or
* debugging.
* @returns {Promise<{ device?: any, response?: any }> | void} Promise if no callback function was passed, void otherwise.
*/
invokeOn(deviceId: string, done: TripleValueCallback<any, any>): void;
invokeOn(deviceId: string): Promise<{ device?: any; response?: any }>;
invokeOn(deviceId: string, done?: TripleValueCallback<any, any>): Promise<{ device?: any; response?: any }> | void {
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_008: [The `invokeOn` method shall throw a `ReferenceError` if `deviceId` is `null`, `undefined` or an empty string.]*/
if (deviceId === null || deviceId === undefined || deviceId === '') throw new ReferenceError('deviceId cannot be \'' + deviceId + '\'');
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_017: [The `invokeOn` method shall uri-encode the device id.]*/
const path = '/twins/' + encodeURIComponent(deviceId) + '/methods' + versionQueryString();
const headers = {
'Content-Type': 'application/json; charset=utf-8'
};
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_011: [The `invokeOn` method shall construct an HTTP request using information supplied by the caller, as follows:
```
POST /twins/<deviceId>/methods?api-version=<version> HTTP/1.1
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
{
"methodName": <DeviceMethod.params.name>,
"responseTimeoutInSeconds": <DeviceMethod.params.responseTimeoutInSeconds>,
"connectTimeoutInSeconds": <DeviceMethod.params.connectTimeoutInSeconds>,
"payload": <DeviceMethod.params.payload>
}
```]*/
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_009: [The `invokeOn` method shall invoke the `_callback` callback with an standard javascript `Error` object if the method execution failed.]*/
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_010: [The `invokeOn` method shall invoke the `_callback` callback with a `null` first argument, a result second argument and a transport-specific response third argument if the method execution succeed**/
const totalTimeout = (this.params.responseTimeoutInSeconds + this.params.connectTimeoutInSeconds) * 1000;
this._client.executeApiCall('POST', path, headers, this.params, totalTimeout, _callback);
}, (d, r) => {
return { device: d, response: r };
}, done);
}
/**
* @method module:azure-iothub.DeviceMethod.invokeOnModule
* @description Invokes the method on the specified module with the specified payload.
* @param {String} deviceId Identifier of the device on which the method will run.
* @param {String} moduleId Identifier of the module on which the method will run.
* @param {Function} [done] The optional function to call when the operation is
* complete. done` will be called with three
* arguments: an Error object (can be null), the
* body of the response, and a transport-specific
* response object useful for logging or
* debugging.
* @returns {Promise<{ device?: any, response?: any }> | void} Promise if no callback function was passed, void otherwise.
*/
invokeOnModule(deviceId: string, moduleId: string, done: TripleValueCallback<any, any>): void;
invokeOnModule(deviceId: string, moduleId: string): Promise<{ device?: any; response?: any }>;
invokeOnModule(deviceId: string, moduleId: string, done?: TripleValueCallback<any, any>): Promise<{ device?: any; response?: any }> | void {
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_18_001: [The `invokeOnModule` method shall throw a `ReferenceError` if `deviceId` or `moduleId` is falsy. ]*/
if (!deviceId) throw new ReferenceError('deviceId cannot be \'' + deviceId + '\'');
if (!moduleId) throw new ReferenceError('moduleId cannot be \'' + moduleId + '\'');
const path = '/twins/' + encodeURIComponent(deviceId) + '/modules/' + encodeURIComponent(moduleId) + '/methods' + versionQueryString();
const headers = {
'Content-Type': 'application/json; charset=utf-8'
};
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_18_002: [The `invokeOnModule` method shall construct an HTTP request using information supplied by the caller, as follows:
```
POST /twins/<encodeUriComponent(deviceId)>/modules/<encodeUriComponent(moduleId)>/methods?api-version=<version> HTTP/1.1
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
{
"methodName": <DeviceMethod.params.name>,
"responseTimeoutInSeconds": <DeviceMethod.params.responseTimeoutInSeconds>,
"connectTimeoutInSeconds": <DeviceMethod.params.connectTimeoutInSeconds>,
"payload": <DeviceMethod.params.payload>
}
```
]*/
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_18_003: [The `invokeOnModule` method shall invoke the `_callback` callback with an standard javascript `Error` object if the method execution failed. ]*/
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_18_004: [The `invokeOnModule` method shall invoke the `_callback` callback with a `null` first argument, a result second argument and a transport-specific response third argument if the method execution succeeds. ]*/
const totalTimeout = (this.params.responseTimeoutInSeconds + this.params.connectTimeoutInSeconds) * 1000;
this._client.executeApiCall('POST', path, headers, this.params, totalTimeout, _callback);
}, (d, r) => {
return { device: d, response: r };
}, done);
}
}

40
src/interfaces.ts Normal file
Просмотреть файл

@ -0,0 +1,40 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { IncomingMessage } from 'http';
import { TripleValueCallback } from 'azure-iot-common';
export type IncomingMessageCallback<TResult> = TripleValueCallback<TResult, IncomingMessage>;
export type ResultWithIncomingMessage<TResult> = {
result: TResult;
message: IncomingMessage;
};
export function createResultWithIncomingMessage<TResult>(result: TResult, message: IncomingMessage): ResultWithIncomingMessage<TResult> {
return { result: result, message: message };
}
/**
* Describes the parameters that are available for use with direct methods (also called device methods)
*/
export interface DeviceMethodParams {
/**
* The name of the method to call on the device.
*/
methodName: string;
/**
* The method payload that will be sent to the device.
*/
payload?: any;
/**
* The maximum time a device should take to respond to the method.
*/
responseTimeoutInSeconds?: number;
/**
* The maximum time the service should try to connect to the device before declaring the device is unreachable.
*/
connectTimeoutInSeconds?: number; // default is 0
}

471
src/job_client.ts Normal file
Просмотреть файл

@ -0,0 +1,471 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { Agent } from 'https';
import * as ConnectionString from './connection_string';
import * as SharedAccessSignature from './shared_access_signature';
import { RestApiClient } from 'azure-iot-http-base';
import { DeviceMethod } from './device_method';
import { Query } from './query';
import { DeviceMethodParams } from './interfaces';
import { TripleValueCallback, tripleValueCallbackToPromise } from 'azure-iot-common';
import { TokenCredential } from '@azure/core-http';
import { versionQueryString } from './version';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('../package.json');
const defaultMaxExecutionTimeInSeconds = 3600;
export type JobType = 'scheduleUpdateTwin' | 'scheduleDeviceMethod';
export type JobStatus = 'queued' | 'scheduled' | 'running' | 'cancelled' | 'finished';
/**
* @private
*/
export interface JobDescription {
jobId: string | number;
type: JobType;
queryCondition?: string;
updateTwin?: any;
cloudToDeviceMethod?: DeviceMethodParams;
startTime: string;
maxExecutionTimeInSeconds: number;
}
/**
* Provides methods to create, update, monitor and cancel long-running jobs on an IoT Hub instance, as well as query existing jobs.
* The Jobs API in Azure IoT Hub allows to schedule direct method calls and twin updates on multiple devices.
*
* SDK users are expected to create {@link azure-iothub.JobClient} instances using the factory methods {@link azure-iothub.JobClient.fromConnectionString} and {@link azure-iothub.JobClient.fromSharedAccessSignature}.
*/
export class JobClient {
private _restApiClient: RestApiClient;
/**
* @private
* @constructor
* @param {RestApiClient} restApiClient The HTTP registry client used to execute REST API calls.@constructor
* @throws {ReferenceError} If the restApiClient argument is falsy.
*/
constructor(restApiClient: RestApiClient) {
/*Codes_SRS_NODE_JOB_CLIENT_16_001: [The `JobClient` constructor shall throw a `ReferenceError` if `restApiClient` is falsy.]*/
if (!restApiClient) throw new ReferenceError('restApiClient cannot be \'' + restApiClient + '\'');
this._restApiClient = restApiClient;
if (this._restApiClient.setOptions) {
this._restApiClient.setOptions({ http: { agent: new Agent({ keepAlive: true }) } });
}
}
/**
* @method module:azure-iothub.JobClient#getJob
* @description Requests information about an existing job.
*
* @param {String} jobId The identifier of an existing job.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be called with three
* arguments: an Error object (can be null), a
* job object, and a transport-specific response
* object useful for logging or debugging.
* @returns {Promise<JobStatusResponse>> | void} Promise if no callback function was passed, void otherwise.
*/
getJob(jobId: string | number, done: TripleValueCallback<any, any>): void;
getJob(jobId: string | number): Promise<JobStatusResponse>;
getJob(jobId: string | number, done?: TripleValueCallback<any, any>): Promise<JobStatusResponse> | void {
/*Codes_SRS_NODE_JOB_CLIENT_16_006: [The `getJob` method shall throw a `ReferenceError` if `jobId` is `null`, `undefined` or an empty string.]*/
return tripleValueCallbackToPromise((_callback) => {
if (jobId === undefined || jobId === null || jobId === '') throw new ReferenceError('jobId cannot be \'' + jobId + '\'');
/*Codes_SRS_NODE_JOB_CLIENT_16_007: [The `getJob` method shall construct the HTTP request as follows:
```
GET /jobs/v2/<jobId>?api-version=<version>
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
```]*/
const path = '/jobs/v2/' + jobId + versionQueryString();
this._restApiClient.executeApiCall('GET', path, null, null, _callback);
}, createJobStatusResponse, done);
}
/**
* @method module:azure-iothub.JobClient#createQuery
* @description Creates a query that can be used to return pages of existing job based on type and status.
*
* @param {String} jobType The type that should be used to filter results.
* @param {String} jobStatus The status that should be used to filter results.
* @param {Number} pageSize The number of elements to return per page.
*/
createQuery(jobType?: JobType, jobStatus?: JobStatus, pageSize?: number): Query {
return new Query(this._getJobsFunc(jobType, jobStatus, pageSize));
}
/**
* @method module:azure-iothub.JobClient#cancelJob
* @description Cancels an existing job.
*
* @param {String} jobId The identifier of an existing job.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be called with three
* arguments: an Error object (can be null), a
* job object, and a transport-specific response
* object useful for logging or debugging.
* @returns {Promise<JobStatusResponse>> | void} Promise if no callback function was passed, void otherwise.
*/
cancelJob(jobId: string | number, done: TripleValueCallback<any, any>): void;
cancelJob(jobId: string | number): Promise<JobStatusResponse>;
cancelJob(jobId: string | number, done?: TripleValueCallback<any, any>): Promise<JobStatusResponse> | void {
/*Codes_SRS_NODE_JOB_CLIENT_16_008: [The `cancelJob` method shall throw a `ReferenceError` if `jobId` is `null`, `undefined` or an empty string.]*/
return tripleValueCallbackToPromise((_callback) => {
if (jobId === undefined || jobId === null || jobId === '') throw new ReferenceError('jobId cannot be \'' + jobId + '\'');
/*Codes_SRS_NODE_JOB_CLIENT_16_009: [The `cancelJob` method shall construct the HTTP request as follows:
```
POST /jobs/v2/<jobId>/cancel?api-version=<version>
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
```]*/
const path = '/jobs/v2/' + jobId + '/cancel' + versionQueryString();
this._restApiClient.executeApiCall('POST', path, null, null, _callback);
}, createJobStatusResponse, done);
}
/**
* @method module:azure-iothub.JobClient#scheduleDeviceMethod
* @description Schedules a job that will execute a device method on a set of devices.
*
* @param {String} jobId The unique identifier that should be used for this job.
* @param {String} queryCondition A SQL query WHERE clause used to compute the list of devices
* on which this job should be run.
* @param {Object} methodParams An object describing the method and shall have the following properties:
* - methodName The name of the method that shall be invoked.
* - payload [optional] The payload to use for the method call.
* - responseTimeoutInSeconds [optional] The number of seconds IoT Hub shall wait for the device
* @param {Date} [jobStartTime] Time time at which the job should start
* @param {Number} [maxExecutionTimeInSeconds] The maximum time alloted for this job to run in seconds.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be called with three
* arguments: an Error object (can be null), a
* job object, and a transport-specific response
* object useful for logging or debugging.
* @returns {Promise<JobStatusResponse>> | void} Promise if no callback function was passed, void otherwise.
*
* @throws {ReferenceError} If one or more of the jobId, queryCondition or methodParams arguments are falsy.
* @throws {ReferenceError} If methodParams.methodName is falsy.
* @throws {TypeError} If the callback is not the last parameter
*/
scheduleDeviceMethod(jobId: string | number, queryCondition: string, methodParams: DeviceMethodParams, jobStartTime: Date, maxExecutionTimeInSeconds: number, done: TripleValueCallback<any, any>): void;
scheduleDeviceMethod(jobId: string | number, queryCondition: string, methodParams: DeviceMethodParams, jobStartTime: Date, done: TripleValueCallback<any, any>): void;
scheduleDeviceMethod(jobId: string | number, queryCondition: string, methodParams: DeviceMethodParams, done: TripleValueCallback<any, any>): void;
scheduleDeviceMethod(jobId: string | number, queryCondition: string, methodParams: DeviceMethodParams, jobStartTime: Date, maxExecutionTimeInSeconds: number): Promise<JobStatusResponse>;
scheduleDeviceMethod(jobId: string | number, queryCondition: string, methodParams: DeviceMethodParams, jobStartTime: Date): Promise<JobStatusResponse>;
scheduleDeviceMethod(jobId: string | number, queryCondition: string, methodParams: DeviceMethodParams): Promise<JobStatusResponse>;
scheduleDeviceMethod(jobId: string | number, queryCondition: string, methodParams: DeviceMethodParams, jobStartTime?: Date | TripleValueCallback<any, any>, maxExecutionTimeInSeconds?: number | TripleValueCallback<any, any>, done?: TripleValueCallback<any, any>): Promise<JobStatusResponse> | void {
const callback = (typeof jobStartTime === 'function') ? jobStartTime as TripleValueCallback<any, any> : ((typeof maxExecutionTimeInSeconds === 'function') ? maxExecutionTimeInSeconds as TripleValueCallback<any, any> : done);
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_JOB_CLIENT_16_013: [The `scheduleDeviceMethod` method shall throw a `ReferenceError` if `jobId` is `null`, `undefined` or an empty string.]*/
if (jobId === undefined || jobId === null || jobId === '') throw new ReferenceError('jobId cannot be \'' + jobId + '\'');
/*Codes_SRS_NODE_JOB_CLIENT_16_014: [The `scheduleDeviceMethod` method shall throw a `ReferenceError` if `queryCondition` is falsy.]*/
if (!queryCondition) throw new ReferenceError('queryCondition cannot be \'' + queryCondition + '\'');
/*Codes_SRS_NODE_JOB_CLIENT_16_029: [The `scheduleDeviceMethod` method shall throw a `ReferenceError` if `methodParams` is falsy.*/
if (!methodParams) throw new ReferenceError('methodParams cannot be \'' + methodParams + '\'');
/*Codes_SRS_NODE_JOB_CLIENT_16_015: [The `scheduleDeviceMethod` method shall throw a `ReferenceError` if `methodParams.methodName` is `null`, `undefined` or an empty string.]*/
if (methodParams.methodName === undefined || methodParams.methodName === null || methodParams.methodName === '') throw new ReferenceError('methodParams.methodName cannot be \'' + methodParams.methodName + '\'');
/*Codes_SRS_NODE_JOB_CLIENT_16_018: [If `jobStartTime` is a function, `jobStartTime` shall be considered the callback and a `TypeError` shall be thrown if `maxExecutionTimeInSeconds` and/or `_callback` are not `undefined`.]*/
if (typeof jobStartTime === 'function') {
if (maxExecutionTimeInSeconds || done) {
throw new TypeError('The callback must be the last parameter');
} else {
_callback = jobStartTime;
jobStartTime = new Date();
maxExecutionTimeInSeconds = defaultMaxExecutionTimeInSeconds;
}
/*Codes_SRS_NODE_JOB_CLIENT_16_019: [If `maxExecutionTimeInSeconds` is a function, `maxExecutionTimeInSeconds` shall be considered the callback and a `TypeError` shall be thrown if `_callback` is not `undefined`.]*/
} else if (typeof maxExecutionTimeInSeconds === 'function') {
if (done) {
throw new TypeError('The callback must be the last parameter');
} else {
_callback = maxExecutionTimeInSeconds;
maxExecutionTimeInSeconds = defaultMaxExecutionTimeInSeconds;
}
}
/*Codes_SRS_NODE_JOB_CLIENT_16_030: [The `scheduleDeviceMethod` method shall use the `DeviceMethod.defaultPayload` value if `methodParams.payload` is `undefined`.]*/
/*Codes_SRS_NODE_JOB_CLIENT_16_031: [The `scheduleDeviceMethod` method shall use the `DeviceMethod.defaultTimeout` value if `methodParams.responseTimeoutInSeconds` is falsy.]*/
const fullMethodParams: DeviceMethodParams = {
methodName: methodParams.methodName,
payload: methodParams.payload || DeviceMethod.defaultPayload,
responseTimeoutInSeconds: methodParams.responseTimeoutInSeconds || DeviceMethod.defaultResponseTimeout
};
/*Codes_SRS_NODE_JOB_CLIENT_16_020: [The `scheduleDeviceMethod` method shall construct the HTTP request as follows:
```
PUT /jobs/v2/<jobId>?api-version=<version>
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
{
jobId: '<jobId>',
type: 'scheduleDirectRequest', // TBC
cloudToDeviceMethod: {
methodName: '<methodName>',
payload: <payload>, // valid JSON object
timeoutInSeconds: methodTimeoutInSeconds // Number
},
queryCondition: '<queryCondition>', // if the query parameter is a string
startTime: <jobStartTime>, // as an ISO-8601 date string
maxExecutionTimeInSeconds: <maxExecutionTimeInSeconds> // format TBD
}
```]*/
const jobDesc: JobDescription = {
jobId: jobId,
type: 'scheduleDeviceMethod',
cloudToDeviceMethod: fullMethodParams,
startTime: jobStartTime ? (jobStartTime as Date).toISOString() : null,
maxExecutionTimeInSeconds: maxExecutionTimeInSeconds as number
};
if (typeof queryCondition === 'string') {
jobDesc.queryCondition = queryCondition;
} else {
throw new TypeError('queryCondition must be a sql WHERE clause string');
}
this._scheduleJob(jobDesc, _callback);
}, createJobStatusResponse, callback);
}
/**
* @method module:azure-iothub.JobClient#scheduleTwinUpdate
* @description Schedule a job that will update a set of twins with the patch provided as a parameter.
*
* @param {String} jobId The unique identifier that should be used for this job.
* @param {String} queryCondition A SQL query WHERE clause used to compute the list of devices
* on which this job should be run.
* @param {Object} patch The twin patch that should be applied to the twins.
* @param {Date} [jobStartTime] Time time at which the job should start
* @param {Number} [maxExecutionTimeInSeconds] The maximum time alloted for this job to run in seconds.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be called with three
* arguments: an Error object (can be null), a
* job object, and a transport-specific response
* object useful for logging or debugging.
* @returns {Promise<JobStatusResponse>> | void} Promise if no callback function was passed, void otherwise.
*
* @throws {ReferenceError} If one or more of the jobId, queryCondition or patch arguments are falsy.
* @throws {TypeError} If the callback is not the last parameter
*/
scheduleTwinUpdate(jobId: string | number, queryCondition: string, patch: any, jobStartTime: Date, maxExecutionTimeInSeconds: number, done: TripleValueCallback<any, any>): void;
scheduleTwinUpdate(jobId: string | number, queryCondition: string, patch: any, jobStartTime: Date, done: TripleValueCallback<any, any>): void;
scheduleTwinUpdate(jobId: string | number, queryCondition: string, patch: any, done: TripleValueCallback<any, any>): void;
scheduleTwinUpdate(jobId: string | number, queryCondition: string, patch: any, jobStartTime: Date, maxExecutionTimeInSeconds?: number): Promise<JobStatusResponse>;
scheduleTwinUpdate(jobId: string | number, queryCondition: string, patch: any, jobStartTime: Date): Promise<JobStatusResponse>;
scheduleTwinUpdate(jobId: string | number, queryCondition: string, patch: any): Promise<JobStatusResponse>;
scheduleTwinUpdate(jobId: string | number, queryCondition: string, patch: any, jobStartTime?: Date | TripleValueCallback<any, any>, maxExecutionTimeInSeconds?: number | TripleValueCallback<any, any>, done?: TripleValueCallback<any, any>): Promise<JobStatusResponse> | void {
const callback = (typeof jobStartTime === 'function') ? jobStartTime as TripleValueCallback<any, any> : ((typeof maxExecutionTimeInSeconds === 'function') ? maxExecutionTimeInSeconds as TripleValueCallback<any, any> : done);
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_JOB_CLIENT_16_021: [The `scheduleTwinUpdate` method shall throw a `ReferenceError` if `jobId` is `null`, `undefined` or an empty string.]*/
if (jobId === undefined || jobId === null || jobId === '') throw new ReferenceError('jobId cannot be \'' + jobId + '\'');
/*Codes_SRS_NODE_JOB_CLIENT_16_022: [The `scheduleTwinUpdate` method shall throw a `ReferenceError` if `query` is falsy.]*/
if (!queryCondition) throw new ReferenceError('queryCondition cannot be \'' + queryCondition + '\'');
/*Codes_SRS_NODE_JOB_CLIENT_16_023: [The `scheduleTwinUpdate` method shall throw a `ReferenceError` if `patch` is falsy.]*/
if (!patch) throw new ReferenceError('patch cannot be \'' + patch + '\'');
/*Codes_SRS_NODE_JOB_CLIENT_16_024: [If `jobStartTime` is a function, `jobStartTime` shall be considered the callback and a `TypeError` shall be thrown if `maxExecutionTimeInSeconds` and/or `_callback` are not `undefined`.]*/
if (typeof jobStartTime === 'function') {
if (maxExecutionTimeInSeconds || done) {
throw new TypeError('The callback must be the last parameter');
} else {
_callback = jobStartTime;
jobStartTime = new Date();
maxExecutionTimeInSeconds = defaultMaxExecutionTimeInSeconds;
}
/*Codes_SRS_NODE_JOB_CLIENT_16_025: [If `maxExecutionTimeInSeconds` is a function, `maxExecutionTimeInSeconds` shall be considered the callback and a `TypeError` shall be thrown if `_callback` is not `undefined`.]*/
} else if (typeof maxExecutionTimeInSeconds === 'function') {
if (done) {
throw new TypeError('The callback must be the last parameter');
} else {
_callback = maxExecutionTimeInSeconds;
maxExecutionTimeInSeconds = defaultMaxExecutionTimeInSeconds;
}
}
const jobDesc: JobDescription = {
jobId: jobId,
type: 'scheduleUpdateTwin',
updateTwin: patch,
startTime: jobStartTime ? (jobStartTime as Date).toISOString() : null,
maxExecutionTimeInSeconds: maxExecutionTimeInSeconds as number
};
if (typeof queryCondition === 'string') {
jobDesc.queryCondition = queryCondition;
} else {
throw new TypeError('queryCondition must be a sql WHERE clause string');
}
/*Codes_SRS_NODE_JOB_CLIENT_16_026: [The `scheduleTwinUpdate` method shall construct the HTTP request as follows:
```
PUT /jobs/v2/<jobId>?api-version=<version>
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
{
jobId: '<jobId>',
type: 'scheduleTwinUpdate', // TBC
updateTwin: <patch> // Valid JSON object
queryCondition: '<queryCondition>', // if the query parameter is a string
startTime: <jobStartTime>, // as an ISO-8601 date string
maxExecutionTimeInSeconds: <maxExecutionTimeInSeconds> // format TBD
}
```]*/
this._scheduleJob(jobDesc, _callback);
}, createJobStatusResponse, callback);
}
private _getJobsFunc(jobType: JobType, jobStatus: JobStatus, pageSize: number): (continuationToken: string, done: TripleValueCallback<any, any>) => void {
/*Codes_SRS_NODE_JOB_CLIENT_16_035: [The `_getJobsFunc` function shall return a function that can be used by the `Query` object to get a new page of results]*/
return (continuationToken, done) => {
/*Codes_SRS_NODE_JOB_CLIENT_16_012: [The `_getJobsFunc` method shall construct the HTTP request as follows:
```
GET /jobs/v2/query?api-version=<version>[&jobType=<jobType>][&jobStatus=<jobStatus>][&pageSize=<pageSize>][&continuationToken=<continuationToken>]
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
```]*/
const jobStatusQueryParam = jobStatus ? '&jobStatus=' + encodeURIComponent(jobStatus) : '';
const jobTypeQueryParam = jobType ? '&jobType=' + encodeURIComponent(jobType) : '';
const path = '/jobs/v2/query' + versionQueryString() + jobStatusQueryParam + jobTypeQueryParam;
const headers = {};
if (continuationToken) {
headers['x-ms-continuation'] = continuationToken;
}
if (pageSize) {
headers['x-ms-max-item-count'] = pageSize;
}
this._restApiClient.executeApiCall('GET', path, headers, null, done);
};
}
private _scheduleJob(jobDesc: JobDescription, done: TripleValueCallback<any, any>): void {
const path = '/jobs/v2/' + encodeURIComponent(jobDesc.jobId.toString()) + versionQueryString();
const headers = {
'Content-Type': 'application/json; charset=utf-8'
};
/*Codes_SRS_NODE_JOB_CLIENT_16_027: [The method shall call the `done` callback with a single argument that is a standard Javascript `Error` object if the request failed.]*/
/*Codes_SRS_NODE_JOB_CLIENT_16_028: [The method shall call the `done` callback with a `null` error argument, a result and a transport-specific response object if the request was successful.]*/
this._restApiClient.executeApiCall('PUT', path, headers, jobDesc, done);
}
/**
* @method module:azure-iothub.JobClient.fromConnectionString
* @description Constructs a JobClient object from the given connection string.
* @static
*
* @param {String} connectionString A connection string which encapsulates the
* appropriate (read and/or write) Registry
* permissions.
*
* @throws {ReferenceError} If the connectionString argument is falsy.
*
* @returns {module:azure-iothub.JobClient}
*/
static fromConnectionString(connectionString: string): JobClient {
/*Codes_SRS_NODE_JOB_CLIENT_16_002: [The `fromConnectionString` method shall throw a `ReferenceError` if `connectionString` is falsy.]*/
if (!connectionString) throw new ReferenceError('connectionString cannot be \'' + connectionString + '\'');
const cn = ConnectionString.parse(connectionString);
const config = {
host: cn.HostName,
sharedAccessSignature: SharedAccessSignature.create(cn.HostName, cn.SharedAccessKeyName, cn.SharedAccessKey, Date.now()),
tokenCredential: undefined
};
/*Codes_SRS_NODE_JOB_CLIENT_16_003: [The `fromConnectionString` method shall return a new `JobClient` instance.]*/
return new JobClient(new RestApiClient(config, packageJson.name + '/' + packageJson.version));
}
/**
* @method module:azure-iothub.JobClient.fromSharedAccessSignature
* @description Constructs a JobClient object from the given shared access signature.
* @static
*
* @param {String} sharedAccessSignature A shared access signature which encapsulates
* the appropriate (read and/or write) Registry
* permissions.
*
* @throws {ReferenceError} If the sharedAccessSignature argument is falsy.
*
* @returns {module:azure-iothub.JobClient}
*/
static fromSharedAccessSignature(sharedAccessSignature: string): JobClient {
/*Codes_SRS_NODE_JOB_CLIENT_16_004: [The `fromSharedAccessSignature` method shall throw a `ReferenceError` if `sharedAccessSignature` is falsy.]*/
if (!sharedAccessSignature) throw new ReferenceError('sharedAccessSignature cannot be \'' + sharedAccessSignature + '\'');
const sas = SharedAccessSignature.parse(sharedAccessSignature);
const config = {
host: sas.sr,
sharedAccessSignature: sharedAccessSignature,
tokenCredential: undefined
};
/*Codes_SRS_NODE_JOB_CLIENT_16_005: [The `fromSharedAccessSignature` method shall return a new `JobClient` instance.]*/
return new JobClient(new RestApiClient(config, packageJson.name + '/' + packageJson.version));
}
/**
* @method module:azure-iothub.JobClient.fromTokenCredential
* @description Constructs a JobClient object from the given shared Azure TokenCredential.
* @static
*
* @param {String} hostName Host name of the Azure service.
* @param {String} tokenCredential An Azure TokenCredential used to authenticate
* with the Azure service
*
* @throws {ReferenceError} If the tokenCredential argument is falsy.
*
* @returns {module:azure-iothub.JobClient}
*/
static fromTokenCredential(hostName: string, tokenCredential: TokenCredential): JobClient {
const config = {
host: hostName,
tokenCredential,
tokenScope: 'https://iothubs.azure.net/.default'
};
return new JobClient(new RestApiClient(config, packageJson.name + '/' + packageJson.version));
}
}
// eslint-disable-next-line no-redeclare
export namespace JobClient {
export type JobCallback = TripleValueCallback<any, any>; // (err: Error, jobStatus?: any, response?: any) => void;
}
export type JobStatusResponse = {
jobStatus: any;
response: any;
};
export function createJobStatusResponse(jobStatus: any, response: any): JobStatusResponse {
return { jobStatus: jobStatus, response: response };
}

59
src/module.ts Normal file
Просмотреть файл

@ -0,0 +1,59 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { Device } from './device';
export interface Module {
/**
* Module ID
*/
moduleId: string;
/**
* Unique device identifier as it exists in the Azure IoT hub device registry.
*/
deviceId: string;
/**
* Used to disambiguate devices that have been deleted/recreated with the same `moduleId`
*/
generationId?: string;
/**
* Weak entity tag assigned to this module identity description
*/
etag?: string;
/**
* Whether the module is 'connected' or 'disconnected'. It is not recommended to use this property to determine if the module is actually connected right now though,
* since the module connection may have timed out and the IoT hub may not have detected it.
*/
connectionState?: Device.ConnectionState;
/**
* Timestamp representing the last time `connectionState` changed.
*/
connectionStateUpdatedTime?: string;
/**
* Timestamp representing the last time the module authenticated, sent a message, or received a message.
*/
lastActivityTime?: string;
/**
* Number of c2d messages waiting to by delivered to the module.
*/
cloudToDeviceMessageCount?: string;
/**
* Contains the symmetric keys used to authenticate this module.
*/
authentication?: Device.Authentication;
/**
* Represents the modules managed by owner
*/
managedBy?: string;
}

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

@ -0,0 +1,56 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "./models";
import * as Mappers from "./models/mappers";
import * as operations from "./operations";
import { IotHubGatewayServiceAPIsContext } from "./iotHubGatewayServiceAPIsContext";
class IotHubGatewayServiceAPIs extends IotHubGatewayServiceAPIsContext {
// Operation groups
configuration: operations.ConfigurationOperations;
statistics: operations.Statistics;
devices: operations.Devices;
bulkRegistry: operations.BulkRegistry;
query: operations.Query;
jobs: operations.Jobs;
cloudToDeviceMessages: operations.CloudToDeviceMessages;
modules: operations.Modules;
digitalTwin: operations.DigitalTwin;
/**
* Initializes a new instance of the IotHubGatewayServiceAPIs class.
* @param credentials Subscription credentials which uniquely identify client subscription.
* @param [options] The parameter options
*/
constructor(credentials: msRest.ServiceClientCredentials, options?: Models.IotHubGatewayServiceAPIsOptions) {
super(credentials, options);
this.configuration = new operations.ConfigurationOperations(this);
this.statistics = new operations.Statistics(this);
this.devices = new operations.Devices(this);
this.bulkRegistry = new operations.BulkRegistry(this);
this.query = new operations.Query(this);
this.jobs = new operations.Jobs(this);
this.cloudToDeviceMessages = new operations.CloudToDeviceMessages(this);
this.modules = new operations.Modules(this);
this.digitalTwin = new operations.DigitalTwin(this);
}
}
// Operation Specifications
export {
IotHubGatewayServiceAPIs,
IotHubGatewayServiceAPIsContext,
Models as IotHubGatewayServiceAPIsModels,
Mappers as IotHubGatewayServiceAPIsMappers
};
export * from "./operations";

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

@ -0,0 +1,50 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "./models";
const packageName = "";
const packageVersion = "";
export class IotHubGatewayServiceAPIsContext extends msRest.ServiceClient {
apiVersion?: string;
credentials: msRest.ServiceClientCredentials;
/**
* Initializes a new instance of the IotHubGatewayServiceAPIsContext class.
* @param credentials Subscription credentials which uniquely identify client subscription.
* @param [options] The parameter options
*/
constructor(credentials: msRest.ServiceClientCredentials, options?: Models.IotHubGatewayServiceAPIsOptions) {
if (credentials == undefined) {
throw new Error("'credentials' cannot be null.");
}
if (!options) {
options = {};
}
if (!options.userAgent) {
const defaultUserAgent = msRest.getDefaultUserAgentValue();
options.userAgent = `${packageName}/${packageVersion} ${defaultUserAgent}`;
}
super(credentials, options);
this.apiVersion = '2020-09-30';
this.baseUri = options.baseUri || this.baseUri || "https://fully-qualified-iothubname.azure-devices.net";
this.requestContentType = "application/json; charset=utf-8";
this.credentials = credentials;
if (options.apiVersion !== null && options.apiVersion !== undefined) {
this.apiVersion = options.apiVersion;
}
}
}

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

@ -0,0 +1,19 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is regenerated.
*/
export {
AuthenticationMechanism,
BulkRegistryOperationResult,
DeviceCapabilities,
DeviceRegistryOperationError,
DeviceRegistryOperationWarning,
ExportImportDevice,
PropertyContainer,
SymmetricKey,
X509Thumbprint
} from "../models/mappers";

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

@ -0,0 +1,11 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is regenerated.
*/
export {
PurgeMessageQueueResult
} from "../models/mappers";

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

@ -0,0 +1,15 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is regenerated.
*/
export {
Configuration,
ConfigurationContent,
ConfigurationMetrics,
ConfigurationQueriesTestInput,
ConfigurationQueriesTestResponse
} from "../models/mappers";

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

@ -0,0 +1,19 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is regenerated.
*/
export {
AuthenticationMechanism,
CloudToDeviceMethod,
CloudToDeviceMethodResult,
Device,
DeviceCapabilities,
SymmetricKey,
Twin,
TwinProperties,
X509Thumbprint
} from "../models/mappers";

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

@ -0,0 +1,14 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is regenerated.
*/
export {
DigitalTwinGetDigitalTwinHeaders,
DigitalTwinInvokeComponentCommandHeaders,
DigitalTwinInvokeRootLevelCommandHeaders,
DigitalTwinUpdateDigitalTwinHeaders
} from "../models/mappers";

2172
src/pl/models/index.ts Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -0,0 +1,20 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is regenerated.
*/
export {
CloudToDeviceMethod,
DeviceCapabilities,
DeviceJobStatistics,
JobProperties,
JobRequest,
JobResponse,
QueryResult,
Twin,
TwinProperties,
X509Thumbprint
} from "../models/mappers";

1460
src/pl/models/mappers.ts Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,19 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is regenerated.
*/
export {
AuthenticationMechanism,
CloudToDeviceMethod,
CloudToDeviceMethodResult,
DeviceCapabilities,
Module,
SymmetricKey,
Twin,
TwinProperties,
X509Thumbprint
} from "../models/mappers";

189
src/pl/models/parameters.ts Normal file
Просмотреть файл

@ -0,0 +1,189 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
export const apiVersion: msRest.OperationQueryParameter = {
parameterPath: "apiVersion",
mapper: {
required: true,
serializedName: "api-version",
defaultValue: '2020-09-30',
type: {
name: "String"
}
}
};
export const commandName: msRest.OperationURLParameter = {
parameterPath: "commandName",
mapper: {
required: true,
serializedName: "commandName",
type: {
name: "String"
}
}
};
export const componentPath: msRest.OperationURLParameter = {
parameterPath: "componentPath",
mapper: {
required: true,
serializedName: "componentPath",
type: {
name: "String"
}
}
};
export const connectTimeoutInSeconds: msRest.OperationQueryParameter = {
parameterPath: [
"options",
"connectTimeoutInSeconds"
],
mapper: {
serializedName: "connectTimeoutInSeconds",
type: {
name: "Number"
}
}
};
export const deviceId: msRest.OperationURLParameter = {
parameterPath: "deviceId",
mapper: {
required: true,
serializedName: "deviceId",
type: {
name: "String"
}
}
};
export const id: msRest.OperationURLParameter = {
parameterPath: "id",
mapper: {
required: true,
serializedName: "id",
type: {
name: "String"
}
}
};
export const ifMatch: msRest.OperationParameter = {
parameterPath: [
"options",
"ifMatch"
],
mapper: {
serializedName: "If-Match",
type: {
name: "String"
}
}
};
export const jobStatus: msRest.OperationQueryParameter = {
parameterPath: [
"options",
"jobStatus"
],
mapper: {
serializedName: "jobStatus",
type: {
name: "String"
}
}
};
export const jobType: msRest.OperationQueryParameter = {
parameterPath: [
"options",
"jobType"
],
mapper: {
serializedName: "jobType",
type: {
name: "String"
}
}
};
export const lockToken: msRest.OperationURLParameter = {
parameterPath: "lockToken",
mapper: {
required: true,
serializedName: "lockToken",
type: {
name: "String"
}
}
};
export const mid: msRest.OperationURLParameter = {
parameterPath: "mid",
mapper: {
required: true,
serializedName: "mid",
type: {
name: "String"
}
}
};
export const moduleId: msRest.OperationURLParameter = {
parameterPath: "moduleId",
mapper: {
required: true,
serializedName: "moduleId",
type: {
name: "String"
}
}
};
export const responseTimeoutInSeconds: msRest.OperationQueryParameter = {
parameterPath: [
"options",
"responseTimeoutInSeconds"
],
mapper: {
serializedName: "responseTimeoutInSeconds",
type: {
name: "Number"
}
}
};
export const top: msRest.OperationQueryParameter = {
parameterPath: [
"options",
"top"
],
mapper: {
serializedName: "top",
type: {
name: "Number"
}
}
};
export const xMsContinuation: msRest.OperationParameter = {
parameterPath: [
"options",
"xMsContinuation"
],
mapper: {
serializedName: "x-ms-continuation",
type: {
name: "String"
}
}
};
export const xMsMaxItemCount: msRest.OperationParameter = {
parameterPath: [
"options",
"xMsMaxItemCount"
],
mapper: {
serializedName: "x-ms-max-item-count",
type: {
name: "String"
}
}
};

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

@ -0,0 +1,16 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is regenerated.
*/
export {
DeviceCapabilities,
QueryGetTwinsHeaders,
QuerySpecification,
Twin,
TwinProperties,
X509Thumbprint
} from "../models/mappers";

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

@ -0,0 +1,12 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is regenerated.
*/
export {
RegistryStatistics,
ServiceStatistics
} from "../models/mappers";

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

@ -0,0 +1,96 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "../models";
import * as Mappers from "../models/bulkRegistryMappers";
import * as Parameters from "../models/parameters";
import { IotHubGatewayServiceAPIsContext } from "../iotHubGatewayServiceAPIsContext";
/** Class representing a BulkRegistry. */
export class BulkRegistry {
private readonly client: IotHubGatewayServiceAPIsContext;
/**
* Create a BulkRegistry.
* @param {IotHubGatewayServiceAPIsContext} client Reference to the service client.
*/
constructor(client: IotHubGatewayServiceAPIsContext) {
this.client = client;
}
/**
* Creates, updates, or deletes the identities of multiple devices from the IoT Hub identity
* registry. A device identity can be specified only once in the list. Different operations
* (create, update, delete) on different devices are allowed. A maximum of 100 devices can be
* specified per invocation. For large scale operations, use the import feature using blob storage
* (https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-identity-registry#import-and-export-device-identities).
* @param devices The registry operations to perform.
* @param [options] The optional parameters
* @returns Promise<Models.BulkRegistryUpdateRegistryResponse>
*/
updateRegistry(devices: Models.ExportImportDevice[], options?: msRest.RequestOptionsBase): Promise<Models.BulkRegistryUpdateRegistryResponse>;
/**
* @param devices The registry operations to perform.
* @param callback The callback
*/
updateRegistry(devices: Models.ExportImportDevice[], callback: msRest.ServiceCallback<Models.BulkRegistryOperationResult>): void;
/**
* @param devices The registry operations to perform.
* @param options The optional parameters
* @param callback The callback
*/
updateRegistry(devices: Models.ExportImportDevice[], options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.BulkRegistryOperationResult>): void;
updateRegistry(devices: Models.ExportImportDevice[], options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.BulkRegistryOperationResult>, callback?: msRest.ServiceCallback<Models.BulkRegistryOperationResult>): Promise<Models.BulkRegistryUpdateRegistryResponse> {
return this.client.sendOperationRequest(
{
devices,
options
},
updateRegistryOperationSpec,
callback) as Promise<Models.BulkRegistryUpdateRegistryResponse>;
}
}
// Operation Specifications
const serializer = new msRest.Serializer(Mappers);
const updateRegistryOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "devices",
queryParameters: [
Parameters.apiVersion
],
requestBody: {
parameterPath: "devices",
mapper: {
required: true,
serializedName: "devices",
type: {
name: "Sequence",
element: {
type: {
name: "Composite",
className: "ExportImportDevice"
}
}
}
}
},
responses: {
200: {
bodyMapper: Mappers.BulkRegistryOperationResult
},
400: {
bodyMapper: Mappers.BulkRegistryOperationResult
},
default: {}
},
serializer
};

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

@ -0,0 +1,211 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "../models";
import * as Mappers from "../models/cloudToDeviceMessagesMappers";
import * as Parameters from "../models/parameters";
import { IotHubGatewayServiceAPIsContext } from "../iotHubGatewayServiceAPIsContext";
/** Class representing a CloudToDeviceMessages. */
export class CloudToDeviceMessages {
private readonly client: IotHubGatewayServiceAPIsContext;
/**
* Create a CloudToDeviceMessages.
* @param {IotHubGatewayServiceAPIsContext} client Reference to the service client.
*/
constructor(client: IotHubGatewayServiceAPIsContext) {
this.client = client;
}
/**
* Deletes all the pending commands for a device in the IoT Hub.
* @param id The unique identifier of the device.
* @param [options] The optional parameters
* @returns Promise<Models.CloudToDeviceMessagesPurgeCloudToDeviceMessageQueueResponse>
*/
purgeCloudToDeviceMessageQueue(id: string, options?: msRest.RequestOptionsBase): Promise<Models.CloudToDeviceMessagesPurgeCloudToDeviceMessageQueueResponse>;
/**
* @param id The unique identifier of the device.
* @param callback The callback
*/
purgeCloudToDeviceMessageQueue(id: string, callback: msRest.ServiceCallback<Models.PurgeMessageQueueResult>): void;
/**
* @param id The unique identifier of the device.
* @param options The optional parameters
* @param callback The callback
*/
purgeCloudToDeviceMessageQueue(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.PurgeMessageQueueResult>): void;
purgeCloudToDeviceMessageQueue(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.PurgeMessageQueueResult>, callback?: msRest.ServiceCallback<Models.PurgeMessageQueueResult>): Promise<Models.CloudToDeviceMessagesPurgeCloudToDeviceMessageQueueResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
purgeCloudToDeviceMessageQueueOperationSpec,
callback) as Promise<Models.CloudToDeviceMessagesPurgeCloudToDeviceMessageQueueResponse>;
}
/**
* Gets the feedback for cloud-to-device messages. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messaging for more information. This
* capability is only available in the standard tier IoT Hub. For more information, see [Choose the
* right IoT Hub tier](https://aka.ms/scaleyouriotsolution).
* @param [options] The optional parameters
* @returns Promise<msRest.RestResponse>
*/
receiveFeedbackNotification(options?: msRest.RequestOptionsBase): Promise<msRest.RestResponse>;
/**
* @param callback The callback
*/
receiveFeedbackNotification(callback: msRest.ServiceCallback<void>): void;
/**
* @param options The optional parameters
* @param callback The callback
*/
receiveFeedbackNotification(options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<void>): void;
receiveFeedbackNotification(options?: msRest.RequestOptionsBase | msRest.ServiceCallback<void>, callback?: msRest.ServiceCallback<void>): Promise<msRest.RestResponse> {
return this.client.sendOperationRequest(
{
options
},
receiveFeedbackNotificationOperationSpec,
callback);
}
/**
* Completes the cloud-to-device feedback message. A completed message is deleted from the feedback
* queue of the service. See https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messaging
* for more information.
* @param lockToken The lock token obtained when the cloud-to-device message is received. This is
* used to resolve race conditions when completing a feedback message.
* @param [options] The optional parameters
* @returns Promise<msRest.RestResponse>
*/
completeFeedbackNotification(lockToken: string, options?: msRest.RequestOptionsBase): Promise<msRest.RestResponse>;
/**
* @param lockToken The lock token obtained when the cloud-to-device message is received. This is
* used to resolve race conditions when completing a feedback message.
* @param callback The callback
*/
completeFeedbackNotification(lockToken: string, callback: msRest.ServiceCallback<void>): void;
/**
* @param lockToken The lock token obtained when the cloud-to-device message is received. This is
* used to resolve race conditions when completing a feedback message.
* @param options The optional parameters
* @param callback The callback
*/
completeFeedbackNotification(lockToken: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<void>): void;
completeFeedbackNotification(lockToken: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<void>, callback?: msRest.ServiceCallback<void>): Promise<msRest.RestResponse> {
return this.client.sendOperationRequest(
{
lockToken,
options
},
completeFeedbackNotificationOperationSpec,
callback);
}
/**
* Abandons the lock on a cloud-to-device feedback message. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messaging for more information.
* @param lockToken The lock token obtained when the cloud-to-device message is received.
* @param [options] The optional parameters
* @returns Promise<msRest.RestResponse>
*/
abandonFeedbackNotification(lockToken: string, options?: msRest.RequestOptionsBase): Promise<msRest.RestResponse>;
/**
* @param lockToken The lock token obtained when the cloud-to-device message is received.
* @param callback The callback
*/
abandonFeedbackNotification(lockToken: string, callback: msRest.ServiceCallback<void>): void;
/**
* @param lockToken The lock token obtained when the cloud-to-device message is received.
* @param options The optional parameters
* @param callback The callback
*/
abandonFeedbackNotification(lockToken: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<void>): void;
abandonFeedbackNotification(lockToken: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<void>, callback?: msRest.ServiceCallback<void>): Promise<msRest.RestResponse> {
return this.client.sendOperationRequest(
{
lockToken,
options
},
abandonFeedbackNotificationOperationSpec,
callback);
}
}
// Operation Specifications
const serializer = new msRest.Serializer(Mappers);
const purgeCloudToDeviceMessageQueueOperationSpec: msRest.OperationSpec = {
httpMethod: "DELETE",
path: "devices/{id}/commands",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.PurgeMessageQueueResult
},
default: {}
},
serializer
};
const receiveFeedbackNotificationOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "messages/serviceBound/feedback",
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {},
204: {},
default: {}
},
serializer
};
const completeFeedbackNotificationOperationSpec: msRest.OperationSpec = {
httpMethod: "DELETE",
path: "messages/serviceBound/feedback/{lockToken}",
urlParameters: [
Parameters.lockToken
],
queryParameters: [
Parameters.apiVersion
],
responses: {
204: {},
default: {}
},
serializer
};
const abandonFeedbackNotificationOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "messages/serviceBound/feedback/{lockToken}/abandon",
urlParameters: [
Parameters.lockToken
],
queryParameters: [
Parameters.apiVersion
],
responses: {
204: {},
default: {}
},
serializer
};

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

@ -0,0 +1,344 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "../models";
import * as Mappers from "../models/configurationOperationsMappers";
import * as Parameters from "../models/parameters";
import { IotHubGatewayServiceAPIsContext } from "../iotHubGatewayServiceAPIsContext";
/** Class representing a ConfigurationOperations. */
export class ConfigurationOperations {
private readonly client: IotHubGatewayServiceAPIsContext;
/**
* Create a ConfigurationOperations.
* @param {IotHubGatewayServiceAPIsContext} client Reference to the service client.
*/
constructor(client: IotHubGatewayServiceAPIsContext) {
this.client = client;
}
/**
* Gets a configuration on the IoT Hub for automatic device/module management.
* @param id The unique identifier of the configuration.
* @param [options] The optional parameters
* @returns Promise<Models.ConfigurationGetResponse>
*/
get(id: string, options?: msRest.RequestOptionsBase): Promise<Models.ConfigurationGetResponse>;
/**
* @param id The unique identifier of the configuration.
* @param callback The callback
*/
get(id: string, callback: msRest.ServiceCallback<Models.Configuration>): void;
/**
* @param id The unique identifier of the configuration.
* @param options The optional parameters
* @param callback The callback
*/
get(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.Configuration>): void;
get(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.Configuration>, callback?: msRest.ServiceCallback<Models.Configuration>): Promise<Models.ConfigurationGetResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
getOperationSpec,
callback) as Promise<Models.ConfigurationGetResponse>;
}
/**
* Creates or updates a configuration on the IoT Hub for automatic device/module management.
* Configuration identifier and Content cannot be updated.
* @param id The unique identifier of the configuration.
* @param configuration The configuration to be created or updated.
* @param [options] The optional parameters
* @returns Promise<Models.ConfigurationCreateOrUpdateResponse>
*/
createOrUpdate(id: string, configuration: Models.Configuration, options?: Models.ConfigurationCreateOrUpdateOptionalParams): Promise<Models.ConfigurationCreateOrUpdateResponse>;
/**
* @param id The unique identifier of the configuration.
* @param configuration The configuration to be created or updated.
* @param callback The callback
*/
createOrUpdate(id: string, configuration: Models.Configuration, callback: msRest.ServiceCallback<Models.Configuration>): void;
/**
* @param id The unique identifier of the configuration.
* @param configuration The configuration to be created or updated.
* @param options The optional parameters
* @param callback The callback
*/
createOrUpdate(id: string, configuration: Models.Configuration, options: Models.ConfigurationCreateOrUpdateOptionalParams, callback: msRest.ServiceCallback<Models.Configuration>): void;
createOrUpdate(id: string, configuration: Models.Configuration, options?: Models.ConfigurationCreateOrUpdateOptionalParams | msRest.ServiceCallback<Models.Configuration>, callback?: msRest.ServiceCallback<Models.Configuration>): Promise<Models.ConfigurationCreateOrUpdateResponse> {
return this.client.sendOperationRequest(
{
id,
configuration,
options
},
createOrUpdateOperationSpec,
callback) as Promise<Models.ConfigurationCreateOrUpdateResponse>;
}
/**
* Deletes a configuration on the IoT Hub for automatic device/module management.
* @param id The unique identifier of the configuration.
* @param [options] The optional parameters
* @returns Promise<msRest.RestResponse>
*/
deleteMethod(id: string, options?: Models.ConfigurationDeleteMethodOptionalParams): Promise<msRest.RestResponse>;
/**
* @param id The unique identifier of the configuration.
* @param callback The callback
*/
deleteMethod(id: string, callback: msRest.ServiceCallback<void>): void;
/**
* @param id The unique identifier of the configuration.
* @param options The optional parameters
* @param callback The callback
*/
deleteMethod(id: string, options: Models.ConfigurationDeleteMethodOptionalParams, callback: msRest.ServiceCallback<void>): void;
deleteMethod(id: string, options?: Models.ConfigurationDeleteMethodOptionalParams | msRest.ServiceCallback<void>, callback?: msRest.ServiceCallback<void>): Promise<msRest.RestResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
deleteMethodOperationSpec,
callback);
}
/**
* Gets configurations on the IoT Hub for automatic device/module management. Pagination is not
* supported.
* @param [options] The optional parameters
* @returns Promise<Models.ConfigurationGetConfigurationsResponse>
*/
getConfigurations(options?: Models.ConfigurationGetConfigurationsOptionalParams): Promise<Models.ConfigurationGetConfigurationsResponse>;
/**
* @param callback The callback
*/
getConfigurations(callback: msRest.ServiceCallback<Models.Configuration[]>): void;
/**
* @param options The optional parameters
* @param callback The callback
*/
getConfigurations(options: Models.ConfigurationGetConfigurationsOptionalParams, callback: msRest.ServiceCallback<Models.Configuration[]>): void;
getConfigurations(options?: Models.ConfigurationGetConfigurationsOptionalParams | msRest.ServiceCallback<Models.Configuration[]>, callback?: msRest.ServiceCallback<Models.Configuration[]>): Promise<Models.ConfigurationGetConfigurationsResponse> {
return this.client.sendOperationRequest(
{
options
},
getConfigurationsOperationSpec,
callback) as Promise<Models.ConfigurationGetConfigurationsResponse>;
}
/**
* Validates target condition and custom metric queries for a configuration on the IoT Hub.
* @param input The configuration for target condition and custom metric queries.
* @param [options] The optional parameters
* @returns Promise<Models.ConfigurationTestQueriesResponse>
*/
testQueries(input: Models.ConfigurationQueriesTestInput, options?: msRest.RequestOptionsBase): Promise<Models.ConfigurationTestQueriesResponse>;
/**
* @param input The configuration for target condition and custom metric queries.
* @param callback The callback
*/
testQueries(input: Models.ConfigurationQueriesTestInput, callback: msRest.ServiceCallback<Models.ConfigurationQueriesTestResponse>): void;
/**
* @param input The configuration for target condition and custom metric queries.
* @param options The optional parameters
* @param callback The callback
*/
testQueries(input: Models.ConfigurationQueriesTestInput, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.ConfigurationQueriesTestResponse>): void;
testQueries(input: Models.ConfigurationQueriesTestInput, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.ConfigurationQueriesTestResponse>, callback?: msRest.ServiceCallback<Models.ConfigurationQueriesTestResponse>): Promise<Models.ConfigurationTestQueriesResponse> {
return this.client.sendOperationRequest(
{
input,
options
},
testQueriesOperationSpec,
callback) as Promise<Models.ConfigurationTestQueriesResponse>;
}
/**
* Applies the configuration content to an edge device.
* @param id The unique identifier of the edge device.
* @param content The configuration content.
* @param [options] The optional parameters
* @returns Promise<msRest.RestResponse>
*/
applyOnEdgeDevice(id: string, content: Models.ConfigurationContent, options?: msRest.RequestOptionsBase): Promise<msRest.RestResponse>;
/**
* @param id The unique identifier of the edge device.
* @param content The configuration content.
* @param callback The callback
*/
applyOnEdgeDevice(id: string, content: Models.ConfigurationContent, callback: msRest.ServiceCallback<void>): void;
/**
* @param id The unique identifier of the edge device.
* @param content The configuration content.
* @param options The optional parameters
* @param callback The callback
*/
applyOnEdgeDevice(id: string, content: Models.ConfigurationContent, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<void>): void;
applyOnEdgeDevice(id: string, content: Models.ConfigurationContent, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<void>, callback?: msRest.ServiceCallback<void>): Promise<msRest.RestResponse> {
return this.client.sendOperationRequest(
{
id,
content,
options
},
applyOnEdgeDeviceOperationSpec,
callback);
}
}
// Operation Specifications
const serializer = new msRest.Serializer(Mappers);
const getOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "configurations/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.Configuration
},
default: {}
},
serializer
};
const createOrUpdateOperationSpec: msRest.OperationSpec = {
httpMethod: "PUT",
path: "configurations/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
requestBody: {
parameterPath: "configuration",
mapper: {
...Mappers.Configuration,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.Configuration
},
201: {
bodyMapper: Mappers.Configuration
},
default: {}
},
serializer
};
const deleteMethodOperationSpec: msRest.OperationSpec = {
httpMethod: "DELETE",
path: "configurations/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
responses: {
204: {},
default: {}
},
serializer
};
const getConfigurationsOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "configurations",
queryParameters: [
Parameters.top,
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: {
serializedName: "parsedResponse",
type: {
name: "Sequence",
element: {
type: {
name: "Composite",
className: "Configuration"
}
}
}
}
},
default: {}
},
serializer
};
const testQueriesOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "configurations/testQueries",
queryParameters: [
Parameters.apiVersion
],
requestBody: {
parameterPath: "input",
mapper: {
...Mappers.ConfigurationQueriesTestInput,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.ConfigurationQueriesTestResponse
},
default: {}
},
serializer
};
const applyOnEdgeDeviceOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "devices/{id}/applyConfigurationContent",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
requestBody: {
parameterPath: "content",
mapper: {
...Mappers.ConfigurationContent,
required: true
}
},
responses: {
204: {},
default: {}
},
serializer
};

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

@ -0,0 +1,468 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "../models";
import * as Mappers from "../models/devicesMappers";
import * as Parameters from "../models/parameters";
import { IotHubGatewayServiceAPIsContext } from "../iotHubGatewayServiceAPIsContext";
/** Class representing a Devices. */
export class Devices {
private readonly client: IotHubGatewayServiceAPIsContext;
/**
* Create a Devices.
* @param {IotHubGatewayServiceAPIsContext} client Reference to the service client.
*/
constructor(client: IotHubGatewayServiceAPIsContext) {
this.client = client;
}
/**
* Gets the identities of multiple devices from the IoT Hub identity registry. Not recommended. Use
* the IoT Hub query API to retrieve device twin and device identity information. See
* https://docs.microsoft.com/en-us/rest/api/iothub/service/queryiothub and
* https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-query-language for more
* information.
* @param [options] The optional parameters
* @returns Promise<Models.DevicesGetDevicesResponse>
*/
getDevices(options?: Models.DevicesGetDevicesOptionalParams): Promise<Models.DevicesGetDevicesResponse>;
/**
* @param callback The callback
*/
getDevices(callback: msRest.ServiceCallback<Models.Device[]>): void;
/**
* @param options The optional parameters
* @param callback The callback
*/
getDevices(options: Models.DevicesGetDevicesOptionalParams, callback: msRest.ServiceCallback<Models.Device[]>): void;
getDevices(options?: Models.DevicesGetDevicesOptionalParams | msRest.ServiceCallback<Models.Device[]>, callback?: msRest.ServiceCallback<Models.Device[]>): Promise<Models.DevicesGetDevicesResponse> {
return this.client.sendOperationRequest(
{
options
},
getDevicesOperationSpec,
callback) as Promise<Models.DevicesGetDevicesResponse>;
}
/**
* Gets a device from the identity registry of the IoT Hub.
* @param id The unique identifier of the device.
* @param [options] The optional parameters
* @returns Promise<Models.DevicesGetIdentityResponse>
*/
getIdentity(id: string, options?: msRest.RequestOptionsBase): Promise<Models.DevicesGetIdentityResponse>;
/**
* @param id The unique identifier of the device.
* @param callback The callback
*/
getIdentity(id: string, callback: msRest.ServiceCallback<Models.Device>): void;
/**
* @param id The unique identifier of the device.
* @param options The optional parameters
* @param callback The callback
*/
getIdentity(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.Device>): void;
getIdentity(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.Device>, callback?: msRest.ServiceCallback<Models.Device>): Promise<Models.DevicesGetIdentityResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
getIdentityOperationSpec,
callback) as Promise<Models.DevicesGetIdentityResponse>;
}
/**
* Creates or updates the identity of a device in the identity registry of the IoT Hub.
* @param id The unique identifier of the device.
* @param device The contents of the device identity.
* @param [options] The optional parameters
* @returns Promise<Models.DevicesCreateOrUpdateIdentityResponse>
*/
createOrUpdateIdentity(id: string, device: Models.Device, options?: Models.DevicesCreateOrUpdateIdentityOptionalParams): Promise<Models.DevicesCreateOrUpdateIdentityResponse>;
/**
* @param id The unique identifier of the device.
* @param device The contents of the device identity.
* @param callback The callback
*/
createOrUpdateIdentity(id: string, device: Models.Device, callback: msRest.ServiceCallback<Models.Device>): void;
/**
* @param id The unique identifier of the device.
* @param device The contents of the device identity.
* @param options The optional parameters
* @param callback The callback
*/
createOrUpdateIdentity(id: string, device: Models.Device, options: Models.DevicesCreateOrUpdateIdentityOptionalParams, callback: msRest.ServiceCallback<Models.Device>): void;
createOrUpdateIdentity(id: string, device: Models.Device, options?: Models.DevicesCreateOrUpdateIdentityOptionalParams | msRest.ServiceCallback<Models.Device>, callback?: msRest.ServiceCallback<Models.Device>): Promise<Models.DevicesCreateOrUpdateIdentityResponse> {
return this.client.sendOperationRequest(
{
id,
device,
options
},
createOrUpdateIdentityOperationSpec,
callback) as Promise<Models.DevicesCreateOrUpdateIdentityResponse>;
}
/**
* Deletes the identity of a device from the identity registry of the IoT Hub.
* @param id The unique identifier of the device.
* @param [options] The optional parameters
* @returns Promise<msRest.RestResponse>
*/
deleteIdentity(id: string, options?: Models.DevicesDeleteIdentityOptionalParams): Promise<msRest.RestResponse>;
/**
* @param id The unique identifier of the device.
* @param callback The callback
*/
deleteIdentity(id: string, callback: msRest.ServiceCallback<void>): void;
/**
* @param id The unique identifier of the device.
* @param options The optional parameters
* @param callback The callback
*/
deleteIdentity(id: string, options: Models.DevicesDeleteIdentityOptionalParams, callback: msRest.ServiceCallback<void>): void;
deleteIdentity(id: string, options?: Models.DevicesDeleteIdentityOptionalParams | msRest.ServiceCallback<void>, callback?: msRest.ServiceCallback<void>): Promise<msRest.RestResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
deleteIdentityOperationSpec,
callback);
}
/**
* Gets the device twin. See https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-device-twins
* for more information.
* @param id The unique identifier of the device.
* @param [options] The optional parameters
* @returns Promise<Models.DevicesGetTwinResponse>
*/
getTwin(id: string, options?: msRest.RequestOptionsBase): Promise<Models.DevicesGetTwinResponse>;
/**
* @param id The unique identifier of the device.
* @param callback The callback
*/
getTwin(id: string, callback: msRest.ServiceCallback<Models.Twin>): void;
/**
* @param id The unique identifier of the device.
* @param options The optional parameters
* @param callback The callback
*/
getTwin(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.Twin>): void;
getTwin(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.Twin>, callback?: msRest.ServiceCallback<Models.Twin>): Promise<Models.DevicesGetTwinResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
getTwinOperationSpec,
callback) as Promise<Models.DevicesGetTwinResponse>;
}
/**
* Replaces the tags and desired properties of a device twin. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-device-twins for more information.
* @param id The unique identifier of the device.
* @param deviceTwinInfo The device twin info that will replace the existing info.
* @param [options] The optional parameters
* @returns Promise<Models.DevicesReplaceTwinResponse>
*/
replaceTwin(id: string, deviceTwinInfo: Models.Twin, options?: Models.DevicesReplaceTwinOptionalParams): Promise<Models.DevicesReplaceTwinResponse>;
/**
* @param id The unique identifier of the device.
* @param deviceTwinInfo The device twin info that will replace the existing info.
* @param callback The callback
*/
replaceTwin(id: string, deviceTwinInfo: Models.Twin, callback: msRest.ServiceCallback<Models.Twin>): void;
/**
* @param id The unique identifier of the device.
* @param deviceTwinInfo The device twin info that will replace the existing info.
* @param options The optional parameters
* @param callback The callback
*/
replaceTwin(id: string, deviceTwinInfo: Models.Twin, options: Models.DevicesReplaceTwinOptionalParams, callback: msRest.ServiceCallback<Models.Twin>): void;
replaceTwin(id: string, deviceTwinInfo: Models.Twin, options?: Models.DevicesReplaceTwinOptionalParams | msRest.ServiceCallback<Models.Twin>, callback?: msRest.ServiceCallback<Models.Twin>): Promise<Models.DevicesReplaceTwinResponse> {
return this.client.sendOperationRequest(
{
id,
deviceTwinInfo,
options
},
replaceTwinOperationSpec,
callback) as Promise<Models.DevicesReplaceTwinResponse>;
}
/**
* Updates the tags and desired properties of a device twin. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-device-twins for more information.
* @param id The unique identifier of the device.
* @param deviceTwinInfo The device twin info containing the tags and desired properties to be
* updated.
* @param [options] The optional parameters
* @returns Promise<Models.DevicesUpdateTwinResponse>
*/
updateTwin(id: string, deviceTwinInfo: Models.Twin, options?: Models.DevicesUpdateTwinOptionalParams): Promise<Models.DevicesUpdateTwinResponse>;
/**
* @param id The unique identifier of the device.
* @param deviceTwinInfo The device twin info containing the tags and desired properties to be
* updated.
* @param callback The callback
*/
updateTwin(id: string, deviceTwinInfo: Models.Twin, callback: msRest.ServiceCallback<Models.Twin>): void;
/**
* @param id The unique identifier of the device.
* @param deviceTwinInfo The device twin info containing the tags and desired properties to be
* updated.
* @param options The optional parameters
* @param callback The callback
*/
updateTwin(id: string, deviceTwinInfo: Models.Twin, options: Models.DevicesUpdateTwinOptionalParams, callback: msRest.ServiceCallback<Models.Twin>): void;
updateTwin(id: string, deviceTwinInfo: Models.Twin, options?: Models.DevicesUpdateTwinOptionalParams | msRest.ServiceCallback<Models.Twin>, callback?: msRest.ServiceCallback<Models.Twin>): Promise<Models.DevicesUpdateTwinResponse> {
return this.client.sendOperationRequest(
{
id,
deviceTwinInfo,
options
},
updateTwinOperationSpec,
callback) as Promise<Models.DevicesUpdateTwinResponse>;
}
/**
* Invokes a direct method on a device. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-direct-methods for more information.
* @param deviceId The unique identifier of the device.
* @param directMethodRequest The parameters to execute a direct method on the device.
* @param [options] The optional parameters
* @returns Promise<Models.DevicesInvokeMethodResponse>
*/
invokeMethod(deviceId: string, directMethodRequest: Models.CloudToDeviceMethod, options?: msRest.RequestOptionsBase): Promise<Models.DevicesInvokeMethodResponse>;
/**
* @param deviceId The unique identifier of the device.
* @param directMethodRequest The parameters to execute a direct method on the device.
* @param callback The callback
*/
invokeMethod(deviceId: string, directMethodRequest: Models.CloudToDeviceMethod, callback: msRest.ServiceCallback<Models.CloudToDeviceMethodResult>): void;
/**
* @param deviceId The unique identifier of the device.
* @param directMethodRequest The parameters to execute a direct method on the device.
* @param options The optional parameters
* @param callback The callback
*/
invokeMethod(deviceId: string, directMethodRequest: Models.CloudToDeviceMethod, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.CloudToDeviceMethodResult>): void;
invokeMethod(deviceId: string, directMethodRequest: Models.CloudToDeviceMethod, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.CloudToDeviceMethodResult>, callback?: msRest.ServiceCallback<Models.CloudToDeviceMethodResult>): Promise<Models.DevicesInvokeMethodResponse> {
return this.client.sendOperationRequest(
{
deviceId,
directMethodRequest,
options
},
invokeMethodOperationSpec,
callback) as Promise<Models.DevicesInvokeMethodResponse>;
}
}
// Operation Specifications
const serializer = new msRest.Serializer(Mappers);
const getDevicesOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "devices",
queryParameters: [
Parameters.top,
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: {
serializedName: "parsedResponse",
type: {
name: "Sequence",
element: {
type: {
name: "Composite",
className: "Device"
}
}
}
}
},
default: {}
},
serializer
};
const getIdentityOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "devices/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.Device
},
default: {}
},
serializer
};
const createOrUpdateIdentityOperationSpec: msRest.OperationSpec = {
httpMethod: "PUT",
path: "devices/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
requestBody: {
parameterPath: "device",
mapper: {
...Mappers.Device,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.Device
},
default: {}
},
serializer
};
const deleteIdentityOperationSpec: msRest.OperationSpec = {
httpMethod: "DELETE",
path: "devices/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
responses: {
204: {},
default: {}
},
serializer
};
const getTwinOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "twins/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.Twin
},
default: {}
},
serializer
};
const replaceTwinOperationSpec: msRest.OperationSpec = {
httpMethod: "PUT",
path: "twins/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
requestBody: {
parameterPath: "deviceTwinInfo",
mapper: {
...Mappers.Twin,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.Twin
},
default: {}
},
serializer
};
const updateTwinOperationSpec: msRest.OperationSpec = {
httpMethod: "PATCH",
path: "twins/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
requestBody: {
parameterPath: "deviceTwinInfo",
mapper: {
...Mappers.Twin,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.Twin
},
default: {}
},
serializer
};
const invokeMethodOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "twins/{deviceId}/methods",
urlParameters: [
Parameters.deviceId
],
queryParameters: [
Parameters.apiVersion
],
requestBody: {
parameterPath: "directMethodRequest",
mapper: {
...Mappers.CloudToDeviceMethod,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.CloudToDeviceMethodResult
},
default: {}
},
serializer
};

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

@ -0,0 +1,303 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "../models";
import * as Mappers from "../models/digitalTwinMappers";
import * as Parameters from "../models/parameters";
import { IotHubGatewayServiceAPIsContext } from "../iotHubGatewayServiceAPIsContext";
/** Class representing a DigitalTwin. */
export class DigitalTwin {
private readonly client: IotHubGatewayServiceAPIsContext;
/**
* Create a DigitalTwin.
* @param {IotHubGatewayServiceAPIsContext} client Reference to the service client.
*/
constructor(client: IotHubGatewayServiceAPIsContext) {
this.client = client;
}
/**
* @summary Gets a digital twin.
* @param id Digital Twin ID.
* @param [options] The optional parameters
* @returns Promise<Models.DigitalTwinGetDigitalTwinResponse>
*/
getDigitalTwin(id: string, options?: msRest.RequestOptionsBase): Promise<Models.DigitalTwinGetDigitalTwinResponse>;
/**
* @param id Digital Twin ID.
* @param callback The callback
*/
getDigitalTwin(id: string, callback: msRest.ServiceCallback<any>): void;
/**
* @param id Digital Twin ID.
* @param options The optional parameters
* @param callback The callback
*/
getDigitalTwin(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<any>): void;
getDigitalTwin(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<any>, callback?: msRest.ServiceCallback<any>): Promise<Models.DigitalTwinGetDigitalTwinResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
getDigitalTwinOperationSpec,
callback) as Promise<Models.DigitalTwinGetDigitalTwinResponse>;
}
/**
* @summary Updates a digital twin.
* @param id Digital Twin ID.
* @param digitalTwinPatch json-patch contents to update.
* @param [options] The optional parameters
* @returns Promise<Models.DigitalTwinUpdateDigitalTwinResponse>
*/
updateDigitalTwin(id: string, digitalTwinPatch: any[], options?: Models.DigitalTwinUpdateDigitalTwinOptionalParams): Promise<Models.DigitalTwinUpdateDigitalTwinResponse>;
/**
* @param id Digital Twin ID.
* @param digitalTwinPatch json-patch contents to update.
* @param callback The callback
*/
updateDigitalTwin(id: string, digitalTwinPatch: any[], callback: msRest.ServiceCallback<void>): void;
/**
* @param id Digital Twin ID.
* @param digitalTwinPatch json-patch contents to update.
* @param options The optional parameters
* @param callback The callback
*/
updateDigitalTwin(id: string, digitalTwinPatch: any[], options: Models.DigitalTwinUpdateDigitalTwinOptionalParams, callback: msRest.ServiceCallback<void>): void;
updateDigitalTwin(id: string, digitalTwinPatch: any[], options?: Models.DigitalTwinUpdateDigitalTwinOptionalParams | msRest.ServiceCallback<void>, callback?: msRest.ServiceCallback<void>): Promise<Models.DigitalTwinUpdateDigitalTwinResponse> {
return this.client.sendOperationRequest(
{
id,
digitalTwinPatch,
options
},
updateDigitalTwinOperationSpec,
callback) as Promise<Models.DigitalTwinUpdateDigitalTwinResponse>;
}
/**
* Invoke a digital twin root level command.
* @summary Invoke a digital twin root level command.
* @param id
* @param commandName
* @param payload
* @param [options] The optional parameters
* @returns Promise<Models.DigitalTwinInvokeRootLevelCommandResponse>
*/
invokeRootLevelCommand(id: string, commandName: string, payload: any, options?: Models.DigitalTwinInvokeRootLevelCommandOptionalParams): Promise<Models.DigitalTwinInvokeRootLevelCommandResponse>;
/**
* @param id
* @param commandName
* @param payload
* @param callback The callback
*/
invokeRootLevelCommand(id: string, commandName: string, payload: any, callback: msRest.ServiceCallback<any>): void;
/**
* @param id
* @param commandName
* @param payload
* @param options The optional parameters
* @param callback The callback
*/
invokeRootLevelCommand(id: string, commandName: string, payload: any, options: Models.DigitalTwinInvokeRootLevelCommandOptionalParams, callback: msRest.ServiceCallback<any>): void;
invokeRootLevelCommand(id: string, commandName: string, payload: any, options?: Models.DigitalTwinInvokeRootLevelCommandOptionalParams | msRest.ServiceCallback<any>, callback?: msRest.ServiceCallback<any>): Promise<Models.DigitalTwinInvokeRootLevelCommandResponse> {
return this.client.sendOperationRequest(
{
id,
commandName,
payload,
options
},
invokeRootLevelCommandOperationSpec,
callback) as Promise<Models.DigitalTwinInvokeRootLevelCommandResponse>;
}
/**
* Invoke a digital twin command.
* @summary Invoke a digital twin command.
* @param id
* @param componentPath
* @param commandName
* @param payload
* @param [options] The optional parameters
* @returns Promise<Models.DigitalTwinInvokeComponentCommandResponse>
*/
invokeComponentCommand(id: string, componentPath: string, commandName: string, payload: any, options?: Models.DigitalTwinInvokeComponentCommandOptionalParams): Promise<Models.DigitalTwinInvokeComponentCommandResponse>;
/**
* @param id
* @param componentPath
* @param commandName
* @param payload
* @param callback The callback
*/
invokeComponentCommand(id: string, componentPath: string, commandName: string, payload: any, callback: msRest.ServiceCallback<any>): void;
/**
* @param id
* @param componentPath
* @param commandName
* @param payload
* @param options The optional parameters
* @param callback The callback
*/
invokeComponentCommand(id: string, componentPath: string, commandName: string, payload: any, options: Models.DigitalTwinInvokeComponentCommandOptionalParams, callback: msRest.ServiceCallback<any>): void;
invokeComponentCommand(id: string, componentPath: string, commandName: string, payload: any, options?: Models.DigitalTwinInvokeComponentCommandOptionalParams | msRest.ServiceCallback<any>, callback?: msRest.ServiceCallback<any>): Promise<Models.DigitalTwinInvokeComponentCommandResponse> {
return this.client.sendOperationRequest(
{
id,
componentPath,
commandName,
payload,
options
},
invokeComponentCommandOperationSpec,
callback) as Promise<Models.DigitalTwinInvokeComponentCommandResponse>;
}
}
// Operation Specifications
const serializer = new msRest.Serializer(Mappers);
const getDigitalTwinOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "digitaltwins/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: {
serializedName: "parsedResponse",
type: {
name: "Object"
}
},
headersMapper: Mappers.DigitalTwinGetDigitalTwinHeaders
},
default: {}
},
serializer
};
const updateDigitalTwinOperationSpec: msRest.OperationSpec = {
httpMethod: "PATCH",
path: "digitaltwins/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
requestBody: {
parameterPath: "digitalTwinPatch",
mapper: {
required: true,
serializedName: "digitalTwinPatch",
type: {
name: "Sequence",
element: {
type: {
name: "Object"
}
}
}
}
},
responses: {
202: {
headersMapper: Mappers.DigitalTwinUpdateDigitalTwinHeaders
},
default: {}
},
serializer
};
const invokeRootLevelCommandOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "digitaltwins/{id}/commands/{commandName}",
urlParameters: [
Parameters.id,
Parameters.commandName
],
queryParameters: [
Parameters.apiVersion,
Parameters.connectTimeoutInSeconds,
Parameters.responseTimeoutInSeconds
],
requestBody: {
parameterPath: "payload",
mapper: {
required: true,
serializedName: "payload",
type: {
name: "Object"
}
}
},
responses: {
200: {
bodyMapper: {
serializedName: "parsedResponse",
type: {
name: "Object"
}
},
headersMapper: Mappers.DigitalTwinInvokeRootLevelCommandHeaders
},
default: {}
},
serializer
};
const invokeComponentCommandOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "digitaltwins/{id}/components/{componentPath}/commands/{commandName}",
urlParameters: [
Parameters.id,
Parameters.componentPath,
Parameters.commandName
],
queryParameters: [
Parameters.apiVersion,
Parameters.connectTimeoutInSeconds,
Parameters.responseTimeoutInSeconds
],
requestBody: {
parameterPath: "payload",
mapper: {
required: true,
serializedName: "payload",
type: {
name: "Object"
}
}
},
responses: {
200: {
bodyMapper: {
serializedName: "parsedResponse",
type: {
name: "Object"
}
},
headersMapper: Mappers.DigitalTwinInvokeComponentCommandHeaders
},
default: {}
},
serializer
};

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

@ -0,0 +1,19 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
export * from "./configurationOperations";
export * from "./statistics";
export * from "./devices";
export * from "./bulkRegistry";
export * from "./query";
export * from "./jobs";
export * from "./cloudToDeviceMessages";
export * from "./modules";
export * from "./digitalTwin";

428
src/pl/operations/jobs.ts Normal file
Просмотреть файл

@ -0,0 +1,428 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "../models";
import * as Mappers from "../models/jobsMappers";
import * as Parameters from "../models/parameters";
import { IotHubGatewayServiceAPIsContext } from "../iotHubGatewayServiceAPIsContext";
/** Class representing a Jobs. */
export class Jobs {
private readonly client: IotHubGatewayServiceAPIsContext;
/**
* Create a Jobs.
* @param {IotHubGatewayServiceAPIsContext} client Reference to the service client.
*/
constructor(client: IotHubGatewayServiceAPIsContext) {
this.client = client;
}
/**
* Creates a new import or export job on the IoT Hub. See
* https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-identity-registry#import-and-export-device-identities
* for more information.
* @param jobProperties The job specifications.
* @param [options] The optional parameters
* @returns Promise<Models.JobsCreateImportExportJobResponse>
*/
createImportExportJob(jobProperties: Models.JobProperties, options?: msRest.RequestOptionsBase): Promise<Models.JobsCreateImportExportJobResponse>;
/**
* @param jobProperties The job specifications.
* @param callback The callback
*/
createImportExportJob(jobProperties: Models.JobProperties, callback: msRest.ServiceCallback<Models.JobProperties>): void;
/**
* @param jobProperties The job specifications.
* @param options The optional parameters
* @param callback The callback
*/
createImportExportJob(jobProperties: Models.JobProperties, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.JobProperties>): void;
createImportExportJob(jobProperties: Models.JobProperties, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.JobProperties>, callback?: msRest.ServiceCallback<Models.JobProperties>): Promise<Models.JobsCreateImportExportJobResponse> {
return this.client.sendOperationRequest(
{
jobProperties,
options
},
createImportExportJobOperationSpec,
callback) as Promise<Models.JobsCreateImportExportJobResponse>;
}
/**
* Gets the status of all import and export jobs in the IoT Hub. See
* https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-identity-registry#import-and-export-device-identities
* for more information.
* @param [options] The optional parameters
* @returns Promise<Models.JobsGetImportExportJobsResponse>
*/
getImportExportJobs(options?: msRest.RequestOptionsBase): Promise<Models.JobsGetImportExportJobsResponse>;
/**
* @param callback The callback
*/
getImportExportJobs(callback: msRest.ServiceCallback<Models.JobProperties[]>): void;
/**
* @param options The optional parameters
* @param callback The callback
*/
getImportExportJobs(options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.JobProperties[]>): void;
getImportExportJobs(options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.JobProperties[]>, callback?: msRest.ServiceCallback<Models.JobProperties[]>): Promise<Models.JobsGetImportExportJobsResponse> {
return this.client.sendOperationRequest(
{
options
},
getImportExportJobsOperationSpec,
callback) as Promise<Models.JobsGetImportExportJobsResponse>;
}
/**
* Gets the status of an import or export job in the IoT Hub. See
* https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-identity-registry#import-and-export-device-identities
* for more information.
* @param id The unique identifier of the job.
* @param [options] The optional parameters
* @returns Promise<Models.JobsGetImportExportJobResponse>
*/
getImportExportJob(id: string, options?: msRest.RequestOptionsBase): Promise<Models.JobsGetImportExportJobResponse>;
/**
* @param id The unique identifier of the job.
* @param callback The callback
*/
getImportExportJob(id: string, callback: msRest.ServiceCallback<Models.JobProperties>): void;
/**
* @param id The unique identifier of the job.
* @param options The optional parameters
* @param callback The callback
*/
getImportExportJob(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.JobProperties>): void;
getImportExportJob(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.JobProperties>, callback?: msRest.ServiceCallback<Models.JobProperties>): Promise<Models.JobsGetImportExportJobResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
getImportExportJobOperationSpec,
callback) as Promise<Models.JobsGetImportExportJobResponse>;
}
/**
* Cancels an import or export job in the IoT Hub.
* @param id The unique identifier of the job.
* @param [options] The optional parameters
* @returns Promise<Models.JobsCancelImportExportJobResponse>
*/
cancelImportExportJob(id: string, options?: msRest.RequestOptionsBase): Promise<Models.JobsCancelImportExportJobResponse>;
/**
* @param id The unique identifier of the job.
* @param callback The callback
*/
cancelImportExportJob(id: string, callback: msRest.ServiceCallback<any>): void;
/**
* @param id The unique identifier of the job.
* @param options The optional parameters
* @param callback The callback
*/
cancelImportExportJob(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<any>): void;
cancelImportExportJob(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<any>, callback?: msRest.ServiceCallback<any>): Promise<Models.JobsCancelImportExportJobResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
cancelImportExportJobOperationSpec,
callback) as Promise<Models.JobsCancelImportExportJobResponse>;
}
/**
* Gets details of a scheduled job from the IoT Hub. See
* https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-jobs for more information.
* @param id The unique identifier of the job.
* @param [options] The optional parameters
* @returns Promise<Models.JobsGetScheduledJobResponse>
*/
getScheduledJob(id: string, options?: msRest.RequestOptionsBase): Promise<Models.JobsGetScheduledJobResponse>;
/**
* @param id The unique identifier of the job.
* @param callback The callback
*/
getScheduledJob(id: string, callback: msRest.ServiceCallback<Models.JobResponse>): void;
/**
* @param id The unique identifier of the job.
* @param options The optional parameters
* @param callback The callback
*/
getScheduledJob(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.JobResponse>): void;
getScheduledJob(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.JobResponse>, callback?: msRest.ServiceCallback<Models.JobResponse>): Promise<Models.JobsGetScheduledJobResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
getScheduledJobOperationSpec,
callback) as Promise<Models.JobsGetScheduledJobResponse>;
}
/**
* Creates a new job to schedule twin updates or direct methods on the IoT Hub at a scheduled time.
* See https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-jobs for more information.
* @param id The unique identifier of the job.
* @param jobRequest The job request info.
* @param [options] The optional parameters
* @returns Promise<Models.JobsCreateScheduledJobResponse>
*/
createScheduledJob(id: string, jobRequest: Models.JobRequest, options?: msRest.RequestOptionsBase): Promise<Models.JobsCreateScheduledJobResponse>;
/**
* @param id The unique identifier of the job.
* @param jobRequest The job request info.
* @param callback The callback
*/
createScheduledJob(id: string, jobRequest: Models.JobRequest, callback: msRest.ServiceCallback<Models.JobResponse>): void;
/**
* @param id The unique identifier of the job.
* @param jobRequest The job request info.
* @param options The optional parameters
* @param callback The callback
*/
createScheduledJob(id: string, jobRequest: Models.JobRequest, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.JobResponse>): void;
createScheduledJob(id: string, jobRequest: Models.JobRequest, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.JobResponse>, callback?: msRest.ServiceCallback<Models.JobResponse>): Promise<Models.JobsCreateScheduledJobResponse> {
return this.client.sendOperationRequest(
{
id,
jobRequest,
options
},
createScheduledJobOperationSpec,
callback) as Promise<Models.JobsCreateScheduledJobResponse>;
}
/**
* Cancels a scheduled job on the IoT Hub. See
* https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-jobs for more information.
* @param id The unique identifier of the job.
* @param [options] The optional parameters
* @returns Promise<Models.JobsCancelScheduledJobResponse>
*/
cancelScheduledJob(id: string, options?: msRest.RequestOptionsBase): Promise<Models.JobsCancelScheduledJobResponse>;
/**
* @param id The unique identifier of the job.
* @param callback The callback
*/
cancelScheduledJob(id: string, callback: msRest.ServiceCallback<Models.JobResponse>): void;
/**
* @param id The unique identifier of the job.
* @param options The optional parameters
* @param callback The callback
*/
cancelScheduledJob(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.JobResponse>): void;
cancelScheduledJob(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.JobResponse>, callback?: msRest.ServiceCallback<Models.JobResponse>): Promise<Models.JobsCancelScheduledJobResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
cancelScheduledJobOperationSpec,
callback) as Promise<Models.JobsCancelScheduledJobResponse>;
}
/**
* Gets the information about jobs using an IoT Hub query. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-query-language for more information.
* @param [options] The optional parameters
* @returns Promise<Models.JobsQueryScheduledJobsResponse>
*/
queryScheduledJobs(options?: Models.JobsQueryScheduledJobsOptionalParams): Promise<Models.JobsQueryScheduledJobsResponse>;
/**
* @param callback The callback
*/
queryScheduledJobs(callback: msRest.ServiceCallback<Models.QueryResult>): void;
/**
* @param options The optional parameters
* @param callback The callback
*/
queryScheduledJobs(options: Models.JobsQueryScheduledJobsOptionalParams, callback: msRest.ServiceCallback<Models.QueryResult>): void;
queryScheduledJobs(options?: Models.JobsQueryScheduledJobsOptionalParams | msRest.ServiceCallback<Models.QueryResult>, callback?: msRest.ServiceCallback<Models.QueryResult>): Promise<Models.JobsQueryScheduledJobsResponse> {
return this.client.sendOperationRequest(
{
options
},
queryScheduledJobsOperationSpec,
callback) as Promise<Models.JobsQueryScheduledJobsResponse>;
}
}
// Operation Specifications
const serializer = new msRest.Serializer(Mappers);
const createImportExportJobOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "jobs/create",
queryParameters: [
Parameters.apiVersion
],
requestBody: {
parameterPath: "jobProperties",
mapper: {
...Mappers.JobProperties,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.JobProperties
},
default: {}
},
serializer
};
const getImportExportJobsOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "jobs",
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: {
serializedName: "parsedResponse",
type: {
name: "Sequence",
element: {
type: {
name: "Composite",
className: "JobProperties"
}
}
}
}
},
default: {}
},
serializer
};
const getImportExportJobOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "jobs/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.JobProperties
},
default: {}
},
serializer
};
const cancelImportExportJobOperationSpec: msRest.OperationSpec = {
httpMethod: "DELETE",
path: "jobs/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: {
serializedName: "parsedResponse",
type: {
name: "Object"
}
}
},
204: {},
default: {}
},
serializer
};
const getScheduledJobOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "jobs/v2/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.JobResponse
},
default: {}
},
serializer
};
const createScheduledJobOperationSpec: msRest.OperationSpec = {
httpMethod: "PUT",
path: "jobs/v2/{id}",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
requestBody: {
parameterPath: "jobRequest",
mapper: {
...Mappers.JobRequest,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.JobResponse
},
default: {}
},
serializer
};
const cancelScheduledJobOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "jobs/v2/{id}/cancel",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.JobResponse
},
default: {}
},
serializer
};
const queryScheduledJobsOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "jobs/v2/query",
queryParameters: [
Parameters.jobType,
Parameters.jobStatus,
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.QueryResult
},
default: {}
},
serializer
};

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

@ -0,0 +1,509 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "../models";
import * as Mappers from "../models/modulesMappers";
import * as Parameters from "../models/parameters";
import { IotHubGatewayServiceAPIsContext } from "../iotHubGatewayServiceAPIsContext";
/** Class representing a Modules. */
export class Modules {
private readonly client: IotHubGatewayServiceAPIsContext;
/**
* Create a Modules.
* @param {IotHubGatewayServiceAPIsContext} client Reference to the service client.
*/
constructor(client: IotHubGatewayServiceAPIsContext) {
this.client = client;
}
/**
* Gets the module twin. See https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-device-twins
* for more information.
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param [options] The optional parameters
* @returns Promise<Models.ModulesGetTwinResponse>
*/
getTwin(id: string, mid: string, options?: msRest.RequestOptionsBase): Promise<Models.ModulesGetTwinResponse>;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param callback The callback
*/
getTwin(id: string, mid: string, callback: msRest.ServiceCallback<Models.Twin>): void;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param options The optional parameters
* @param callback The callback
*/
getTwin(id: string, mid: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.Twin>): void;
getTwin(id: string, mid: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.Twin>, callback?: msRest.ServiceCallback<Models.Twin>): Promise<Models.ModulesGetTwinResponse> {
return this.client.sendOperationRequest(
{
id,
mid,
options
},
getTwinOperationSpec,
callback) as Promise<Models.ModulesGetTwinResponse>;
}
/**
* Replaces the tags and desired properties of a module twin. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-device-twins for more information.
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param deviceTwinInfo The module twin info that will replace the existing info.
* @param [options] The optional parameters
* @returns Promise<Models.ModulesReplaceTwinResponse>
*/
replaceTwin(id: string, mid: string, deviceTwinInfo: Models.Twin, options?: Models.ModulesReplaceTwinOptionalParams): Promise<Models.ModulesReplaceTwinResponse>;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param deviceTwinInfo The module twin info that will replace the existing info.
* @param callback The callback
*/
replaceTwin(id: string, mid: string, deviceTwinInfo: Models.Twin, callback: msRest.ServiceCallback<Models.Twin>): void;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param deviceTwinInfo The module twin info that will replace the existing info.
* @param options The optional parameters
* @param callback The callback
*/
replaceTwin(id: string, mid: string, deviceTwinInfo: Models.Twin, options: Models.ModulesReplaceTwinOptionalParams, callback: msRest.ServiceCallback<Models.Twin>): void;
replaceTwin(id: string, mid: string, deviceTwinInfo: Models.Twin, options?: Models.ModulesReplaceTwinOptionalParams | msRest.ServiceCallback<Models.Twin>, callback?: msRest.ServiceCallback<Models.Twin>): Promise<Models.ModulesReplaceTwinResponse> {
return this.client.sendOperationRequest(
{
id,
mid,
deviceTwinInfo,
options
},
replaceTwinOperationSpec,
callback) as Promise<Models.ModulesReplaceTwinResponse>;
}
/**
* Updates the tags and desired properties of a module twin. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-device-twins for more information.
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param deviceTwinInfo The module twin info containing the tags and desired properties to be
* updated.
* @param [options] The optional parameters
* @returns Promise<Models.ModulesUpdateTwinResponse>
*/
updateTwin(id: string, mid: string, deviceTwinInfo: Models.Twin, options?: Models.ModulesUpdateTwinOptionalParams): Promise<Models.ModulesUpdateTwinResponse>;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param deviceTwinInfo The module twin info containing the tags and desired properties to be
* updated.
* @param callback The callback
*/
updateTwin(id: string, mid: string, deviceTwinInfo: Models.Twin, callback: msRest.ServiceCallback<Models.Twin>): void;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param deviceTwinInfo The module twin info containing the tags and desired properties to be
* updated.
* @param options The optional parameters
* @param callback The callback
*/
updateTwin(id: string, mid: string, deviceTwinInfo: Models.Twin, options: Models.ModulesUpdateTwinOptionalParams, callback: msRest.ServiceCallback<Models.Twin>): void;
updateTwin(id: string, mid: string, deviceTwinInfo: Models.Twin, options?: Models.ModulesUpdateTwinOptionalParams | msRest.ServiceCallback<Models.Twin>, callback?: msRest.ServiceCallback<Models.Twin>): Promise<Models.ModulesUpdateTwinResponse> {
return this.client.sendOperationRequest(
{
id,
mid,
deviceTwinInfo,
options
},
updateTwinOperationSpec,
callback) as Promise<Models.ModulesUpdateTwinResponse>;
}
/**
* Gets all the module identities on the device.
* @param id The unique identifier of the device.
* @param [options] The optional parameters
* @returns Promise<Models.ModulesGetModulesOnDeviceResponse>
*/
getModulesOnDevice(id: string, options?: msRest.RequestOptionsBase): Promise<Models.ModulesGetModulesOnDeviceResponse>;
/**
* @param id The unique identifier of the device.
* @param callback The callback
*/
getModulesOnDevice(id: string, callback: msRest.ServiceCallback<Models.Module[]>): void;
/**
* @param id The unique identifier of the device.
* @param options The optional parameters
* @param callback The callback
*/
getModulesOnDevice(id: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.Module[]>): void;
getModulesOnDevice(id: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.Module[]>, callback?: msRest.ServiceCallback<Models.Module[]>): Promise<Models.ModulesGetModulesOnDeviceResponse> {
return this.client.sendOperationRequest(
{
id,
options
},
getModulesOnDeviceOperationSpec,
callback) as Promise<Models.ModulesGetModulesOnDeviceResponse>;
}
/**
* Gets a module identity on the device.
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param [options] The optional parameters
* @returns Promise<Models.ModulesGetIdentityResponse>
*/
getIdentity(id: string, mid: string, options?: msRest.RequestOptionsBase): Promise<Models.ModulesGetIdentityResponse>;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param callback The callback
*/
getIdentity(id: string, mid: string, callback: msRest.ServiceCallback<Models.Module>): void;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param options The optional parameters
* @param callback The callback
*/
getIdentity(id: string, mid: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.Module>): void;
getIdentity(id: string, mid: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.Module>, callback?: msRest.ServiceCallback<Models.Module>): Promise<Models.ModulesGetIdentityResponse> {
return this.client.sendOperationRequest(
{
id,
mid,
options
},
getIdentityOperationSpec,
callback) as Promise<Models.ModulesGetIdentityResponse>;
}
/**
* Creates or updates the module identity for a device in the IoT Hub. The moduleId and
* generationId cannot be updated by the user.
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param module The module identity.
* @param [options] The optional parameters
* @returns Promise<Models.ModulesCreateOrUpdateIdentityResponse>
*/
createOrUpdateIdentity(id: string, mid: string, module: Models.Module, options?: Models.ModulesCreateOrUpdateIdentityOptionalParams): Promise<Models.ModulesCreateOrUpdateIdentityResponse>;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param module The module identity.
* @param callback The callback
*/
createOrUpdateIdentity(id: string, mid: string, module: Models.Module, callback: msRest.ServiceCallback<Models.Module>): void;
/**
* @param id The unique identifier of the device.
* @param mid The unique identifier of the module.
* @param module The module identity.
* @param options The optional parameters
* @param callback The callback
*/
createOrUpdateIdentity(id: string, mid: string, module: Models.Module, options: Models.ModulesCreateOrUpdateIdentityOptionalParams, callback: msRest.ServiceCallback<Models.Module>): void;
createOrUpdateIdentity(id: string, mid: string, module: Models.Module, options?: Models.ModulesCreateOrUpdateIdentityOptionalParams | msRest.ServiceCallback<Models.Module>, callback?: msRest.ServiceCallback<Models.Module>): Promise<Models.ModulesCreateOrUpdateIdentityResponse> {
return this.client.sendOperationRequest(
{
id,
mid,
module,
options
},
createOrUpdateIdentityOperationSpec,
callback) as Promise<Models.ModulesCreateOrUpdateIdentityResponse>;
}
/**
* Deletes the module identity for a device in the IoT Hub.
* @param id The unique identifier of the deivce.
* @param mid The unique identifier of the module.
* @param [options] The optional parameters
* @returns Promise<msRest.RestResponse>
*/
deleteIdentity(id: string, mid: string, options?: Models.ModulesDeleteIdentityOptionalParams): Promise<msRest.RestResponse>;
/**
* @param id The unique identifier of the deivce.
* @param mid The unique identifier of the module.
* @param callback The callback
*/
deleteIdentity(id: string, mid: string, callback: msRest.ServiceCallback<void>): void;
/**
* @param id The unique identifier of the deivce.
* @param mid The unique identifier of the module.
* @param options The optional parameters
* @param callback The callback
*/
deleteIdentity(id: string, mid: string, options: Models.ModulesDeleteIdentityOptionalParams, callback: msRest.ServiceCallback<void>): void;
deleteIdentity(id: string, mid: string, options?: Models.ModulesDeleteIdentityOptionalParams | msRest.ServiceCallback<void>, callback?: msRest.ServiceCallback<void>): Promise<msRest.RestResponse> {
return this.client.sendOperationRequest(
{
id,
mid,
options
},
deleteIdentityOperationSpec,
callback);
}
/**
* Invokes a direct method on a module of a device. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-direct-methods for more information.
* @param deviceId The unique identifier of the device.
* @param moduleId The unique identifier of the module.
* @param directMethodRequest The parameters to execute a direct method on the module.
* @param [options] The optional parameters
* @returns Promise<Models.ModulesInvokeMethodResponse>
*/
invokeMethod(deviceId: string, moduleId: string, directMethodRequest: Models.CloudToDeviceMethod, options?: msRest.RequestOptionsBase): Promise<Models.ModulesInvokeMethodResponse>;
/**
* @param deviceId The unique identifier of the device.
* @param moduleId The unique identifier of the module.
* @param directMethodRequest The parameters to execute a direct method on the module.
* @param callback The callback
*/
invokeMethod(deviceId: string, moduleId: string, directMethodRequest: Models.CloudToDeviceMethod, callback: msRest.ServiceCallback<Models.CloudToDeviceMethodResult>): void;
/**
* @param deviceId The unique identifier of the device.
* @param moduleId The unique identifier of the module.
* @param directMethodRequest The parameters to execute a direct method on the module.
* @param options The optional parameters
* @param callback The callback
*/
invokeMethod(deviceId: string, moduleId: string, directMethodRequest: Models.CloudToDeviceMethod, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.CloudToDeviceMethodResult>): void;
invokeMethod(deviceId: string, moduleId: string, directMethodRequest: Models.CloudToDeviceMethod, options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.CloudToDeviceMethodResult>, callback?: msRest.ServiceCallback<Models.CloudToDeviceMethodResult>): Promise<Models.ModulesInvokeMethodResponse> {
return this.client.sendOperationRequest(
{
deviceId,
moduleId,
directMethodRequest,
options
},
invokeMethodOperationSpec,
callback) as Promise<Models.ModulesInvokeMethodResponse>;
}
}
// Operation Specifications
const serializer = new msRest.Serializer(Mappers);
const getTwinOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "twins/{id}/modules/{mid}",
urlParameters: [
Parameters.id,
Parameters.mid
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.Twin
},
default: {}
},
serializer
};
const replaceTwinOperationSpec: msRest.OperationSpec = {
httpMethod: "PUT",
path: "twins/{id}/modules/{mid}",
urlParameters: [
Parameters.id,
Parameters.mid
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
requestBody: {
parameterPath: "deviceTwinInfo",
mapper: {
...Mappers.Twin,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.Twin
},
default: {}
},
serializer
};
const updateTwinOperationSpec: msRest.OperationSpec = {
httpMethod: "PATCH",
path: "twins/{id}/modules/{mid}",
urlParameters: [
Parameters.id,
Parameters.mid
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
requestBody: {
parameterPath: "deviceTwinInfo",
mapper: {
...Mappers.Twin,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.Twin
},
default: {}
},
serializer
};
const getModulesOnDeviceOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "devices/{id}/modules",
urlParameters: [
Parameters.id
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: {
serializedName: "parsedResponse",
type: {
name: "Sequence",
element: {
type: {
name: "Composite",
className: "Module"
}
}
}
}
},
default: {}
},
serializer
};
const getIdentityOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "devices/{id}/modules/{mid}",
urlParameters: [
Parameters.id,
Parameters.mid
],
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.Module
},
default: {}
},
serializer
};
const createOrUpdateIdentityOperationSpec: msRest.OperationSpec = {
httpMethod: "PUT",
path: "devices/{id}/modules/{mid}",
urlParameters: [
Parameters.id,
Parameters.mid
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
requestBody: {
parameterPath: "module",
mapper: {
...Mappers.Module,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.Module
},
201: {
bodyMapper: Mappers.Module
},
default: {}
},
serializer
};
const deleteIdentityOperationSpec: msRest.OperationSpec = {
httpMethod: "DELETE",
path: "devices/{id}/modules/{mid}",
urlParameters: [
Parameters.id,
Parameters.mid
],
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.ifMatch
],
responses: {
204: {},
default: {}
},
serializer
};
const invokeMethodOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "twins/{deviceId}/modules/{moduleId}/methods",
urlParameters: [
Parameters.deviceId,
Parameters.moduleId
],
queryParameters: [
Parameters.apiVersion
],
requestBody: {
parameterPath: "directMethodRequest",
mapper: {
...Mappers.CloudToDeviceMethod,
required: true
}
},
responses: {
200: {
bodyMapper: Mappers.CloudToDeviceMethodResult
},
default: {}
},
serializer
};

101
src/pl/operations/query.ts Normal file
Просмотреть файл

@ -0,0 +1,101 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "../models";
import * as Mappers from "../models/queryMappers";
import * as Parameters from "../models/parameters";
import { IotHubGatewayServiceAPIsContext } from "../iotHubGatewayServiceAPIsContext";
/** Class representing a Query. */
export class Query {
private readonly client: IotHubGatewayServiceAPIsContext;
/**
* Create a Query.
* @param {IotHubGatewayServiceAPIsContext} client Reference to the service client.
*/
constructor(client: IotHubGatewayServiceAPIsContext) {
this.client = client;
}
/**
* Query an IoT Hub to retrieve information regarding device twins using a SQL-like language. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-query-language for more information.
* Pagination is supported. This returns information about device twins only.
* @param querySpecification The query string. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-query-language for more information.
* @param [options] The optional parameters
* @returns Promise<Models.QueryGetTwinsResponse>
*/
getTwins(querySpecification: Models.QuerySpecification, options?: Models.QueryGetTwinsOptionalParams): Promise<Models.QueryGetTwinsResponse>;
/**
* @param querySpecification The query string. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-query-language for more information.
* @param callback The callback
*/
getTwins(querySpecification: Models.QuerySpecification, callback: msRest.ServiceCallback<Models.Twin[]>): void;
/**
* @param querySpecification The query string. See
* https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-query-language for more information.
* @param options The optional parameters
* @param callback The callback
*/
getTwins(querySpecification: Models.QuerySpecification, options: Models.QueryGetTwinsOptionalParams, callback: msRest.ServiceCallback<Models.Twin[]>): void;
getTwins(querySpecification: Models.QuerySpecification, options?: Models.QueryGetTwinsOptionalParams | msRest.ServiceCallback<Models.Twin[]>, callback?: msRest.ServiceCallback<Models.Twin[]>): Promise<Models.QueryGetTwinsResponse> {
return this.client.sendOperationRequest(
{
querySpecification,
options
},
getTwinsOperationSpec,
callback) as Promise<Models.QueryGetTwinsResponse>;
}
}
// Operation Specifications
const serializer = new msRest.Serializer(Mappers);
const getTwinsOperationSpec: msRest.OperationSpec = {
httpMethod: "POST",
path: "devices/query",
queryParameters: [
Parameters.apiVersion
],
headerParameters: [
Parameters.xMsContinuation,
Parameters.xMsMaxItemCount
],
requestBody: {
parameterPath: "querySpecification",
mapper: {
...Mappers.QuerySpecification,
required: true
}
},
responses: {
200: {
bodyMapper: {
serializedName: "parsedResponse",
type: {
name: "Sequence",
element: {
type: {
name: "Composite",
className: "Twin"
}
}
}
},
headersMapper: Mappers.QueryGetTwinsHeaders
},
default: {}
},
serializer
};

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

@ -0,0 +1,108 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*
* Code generated by Microsoft (R) AutoRest Code Generator.
* Changes may cause incorrect behavior and will be lost if the code is
* regenerated.
*/
import * as msRest from "@azure/ms-rest-js";
import * as Models from "../models";
import * as Mappers from "../models/statisticsMappers";
import * as Parameters from "../models/parameters";
import { IotHubGatewayServiceAPIsContext } from "../iotHubGatewayServiceAPIsContext";
/** Class representing a Statistics. */
export class Statistics {
private readonly client: IotHubGatewayServiceAPIsContext;
/**
* Create a Statistics.
* @param {IotHubGatewayServiceAPIsContext} client Reference to the service client.
*/
constructor(client: IotHubGatewayServiceAPIsContext) {
this.client = client;
}
/**
* Gets device statistics of the IoT Hub identity registry, such as total device count.
* @param [options] The optional parameters
* @returns Promise<Models.StatisticsGetDeviceStatisticsResponse>
*/
getDeviceStatistics(options?: msRest.RequestOptionsBase): Promise<Models.StatisticsGetDeviceStatisticsResponse>;
/**
* @param callback The callback
*/
getDeviceStatistics(callback: msRest.ServiceCallback<Models.RegistryStatistics>): void;
/**
* @param options The optional parameters
* @param callback The callback
*/
getDeviceStatistics(options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.RegistryStatistics>): void;
getDeviceStatistics(options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.RegistryStatistics>, callback?: msRest.ServiceCallback<Models.RegistryStatistics>): Promise<Models.StatisticsGetDeviceStatisticsResponse> {
return this.client.sendOperationRequest(
{
options
},
getDeviceStatisticsOperationSpec,
callback) as Promise<Models.StatisticsGetDeviceStatisticsResponse>;
}
/**
* Gets service statistics of the IoT Hub identity registry, such as connected device count.
* @param [options] The optional parameters
* @returns Promise<Models.StatisticsGetServiceStatisticsResponse>
*/
getServiceStatistics(options?: msRest.RequestOptionsBase): Promise<Models.StatisticsGetServiceStatisticsResponse>;
/**
* @param callback The callback
*/
getServiceStatistics(callback: msRest.ServiceCallback<Models.ServiceStatistics>): void;
/**
* @param options The optional parameters
* @param callback The callback
*/
getServiceStatistics(options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback<Models.ServiceStatistics>): void;
getServiceStatistics(options?: msRest.RequestOptionsBase | msRest.ServiceCallback<Models.ServiceStatistics>, callback?: msRest.ServiceCallback<Models.ServiceStatistics>): Promise<Models.StatisticsGetServiceStatisticsResponse> {
return this.client.sendOperationRequest(
{
options
},
getServiceStatisticsOperationSpec,
callback) as Promise<Models.StatisticsGetServiceStatisticsResponse>;
}
}
// Operation Specifications
const serializer = new msRest.Serializer(Mappers);
const getDeviceStatisticsOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "statistics/devices",
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.RegistryStatistics
},
default: {}
},
serializer
};
const getServiceStatisticsOperationSpec: msRest.OperationSpec = {
httpMethod: "GET",
path: "statistics/service",
queryParameters: [
Parameters.apiVersion
],
responses: {
200: {
bodyMapper: Mappers.ServiceStatistics
},
default: {}
},
serializer
};

135
src/query.ts Normal file
Просмотреть файл

@ -0,0 +1,135 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { Twin } from './twin';
import { Registry } from './registry';
import { IncomingMessageCallback, ResultWithIncomingMessage, createResultWithIncomingMessage } from './interfaces';
import { tripleValueCallbackToPromise } from 'azure-iot-common';
/**
* Constructs a Query object that provides APIs to trigger the execution of a device query.
* SDK users should create queries using the {@link azure-iothub.JobClient.createQuery} and {@link azure-iothub.Registry.createQuery} APIs
* and should not try to instantiate this class directly.
*/
export class Query {
/**
* Token used to iterate over multiple pages of results.
*/
continuationToken: string;
/**
* Boolean indicating whether there are more results pages.
*/
hasMoreResults: boolean;
private _registry: Registry;
private _executeQueryFn: (continuationToken: string, done: IncomingMessageCallback<any>) => void;
/**
* @private
* @constructor
* @param {Function} executeQueryFn The function that should be called to get a new page.
* @param {Registry} registry [optional] Registry client used to create Twin objects (used in nextAsTwin()).
*/
constructor(executeQueryFn: (continuationToken: string, done: IncomingMessageCallback<any>) => void, registry?: Registry) {
if (!executeQueryFn) throw new ReferenceError('executeQueryFn cannot be \'' + executeQueryFn + '\'');
if (typeof executeQueryFn !== 'function') throw new TypeError('executeQueryFn cannot be \'' + typeof executeQueryFn + '\'');
this._executeQueryFn = executeQueryFn;
this._registry = registry;
this.hasMoreResults = true;
this.continuationToken = null;
}
/**
* @method module:azure-iothub.Query#next
* @description Gets the next page of results for this query.
* @param {string} continuationToken Continuation Token used for paging through results (optional)
* @param {Function} [done] The optional callback that will be called with either an Error object or
* the results of the query.
* @returns {Promise<ResultWithIncomingMessage<any>> | void} Promise if no callback function was passed, void otherwise.
*/
next(continuationToken: string, done: IncomingMessageCallback<any>): void;
next(done: IncomingMessageCallback<any>): void;
next(continuationToken: string): Promise<ResultWithIncomingMessage<any>>;
next(): Promise<ResultWithIncomingMessage<any>>;
next(continuationTokenOrCallback?: string | IncomingMessageCallback<any>, done?: IncomingMessageCallback<any>): Promise<ResultWithIncomingMessage<any>> | void {
let actualContinuationToken = this.continuationToken;
let actualCallback: IncomingMessageCallback<any>;
/*Codes_SRS_NODE_SERVICE_QUERY_16_016: [If `continuationToken` is a function and `done` is undefined the `next` method shall assume that `continuationToken` is actually the callback and us it as such (see requirements associated with the `done` parameter)]*/
if (typeof continuationTokenOrCallback === 'function' && !done) {
actualCallback = continuationTokenOrCallback as IncomingMessageCallback<any>;
} else {
/*Codes_SRS_NODE_SERVICE_QUERY_16_017: [the `next` method shall use the `continuationToken` passed as argument instead of its own property `Query.continuationToken` if it's not falsy.]*/
actualContinuationToken = continuationTokenOrCallback as string;
actualCallback = done as IncomingMessageCallback<any>;
}
return tripleValueCallbackToPromise((_callback) => {
this._executeQueryFn(actualContinuationToken, (err, result, response) => {
if (err) {
/*Codes_SRS_NODE_SERVICE_QUERY_16_008: [The `next` method shall call the `_callback` callback with a single argument that is an instance of the standard Javascript `Error` object if the request failed.]*/
_callback(err);
} else {
/*Codes_SRS_NODE_SERVICE_QUERY_16_006: [The `next` method shall set the `Query.continuationToken` property to the `continuationToken` value of the query result.]*/
this.continuationToken = response.headers['x-ms-continuation'] as string;
/*Codes_SRS_NODE_SERVICE_QUERY_16_013: [The `next` method shall set the `Query.hasMoreResults` property to `true` if the `continuationToken` property of the result object is not `null`.]*/
/*Codes_SRS_NODE_SERVICE_QUERY_16_014: [The `next` method shall set the `Query.hasMoreResults` property to `false` if the `continuationToken` property of the result object is `null`.]*/
this.hasMoreResults = this.continuationToken !== undefined;
/*Codes_SRS_NODE_SERVICE_QUERY_16_007: [The `next` method shall call the `_callback` callback with a `null` error object, the results of the query and the response of the underlying transport if the request was successful.]*/
_callback(null, result, response);
}
});
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, actualCallback);
}
/**
* @method module:azure-iothub.Query#nextAsTwin
* @description Gets the next page of results for this query and cast them as Twins.
* @param {string} continuationToken Continuation Token used for paging through results (optional)
* @param {Function} [done] The optional callback that will be called with either an Error object or
* the results of the query.
* @returns {Promise<ResultWithIncomingMessage<Twin[]>> | void} Promise if no callback function was passed, void otherwise.
*/
nextAsTwin(continuationToken: string, done: IncomingMessageCallback<Twin[]>): void;
nextAsTwin(done: IncomingMessageCallback<Twin[]>): void;
nextAsTwin(continuationToken: string): Promise<ResultWithIncomingMessage<Twin[]>>;
nextAsTwin(): Promise<ResultWithIncomingMessage<Twin[]>>;
nextAsTwin(continuationToken?: string | IncomingMessageCallback<Twin[]>, done?: IncomingMessageCallback<Twin[]>): Promise<ResultWithIncomingMessage<Twin[]>> | void {
/*Codes_SRS_NODE_SERVICE_QUERY_16_016: [If `continuationToken` is a function and `_callback` is undefined the `next` method shall assume that `continuationToken` is actually the callback and us it as such (see requirements associated with the `done` parameter)]*/
if (typeof continuationToken === 'function' && !done) {
done = continuationToken;
continuationToken = null;
}
return tripleValueCallbackToPromise((_callback) => {
const ct = continuationToken as string || this.continuationToken;
this.next(ct, (err, result, response) => {
if (err) {
/*Codes_SRS_NODE_SERVICE_QUERY_16_008: [The `next` method shall call the `_callback` callback with a single argument that is an instance of the standard Javascript `Error` object if the request failed.]*/
_callback(err);
} else {
if (result) {
/*SRS_NODE_SERVICE_QUERY_16_009: [The `nextAsTwin` method shall call the `_callback` callback with a `null` error object and a collection of `Twin` objects created from the results of the query if the request was successful.]*/
const twins = result.map((twinJson) => {
return new Twin(twinJson, this._registry);
});
_callback(null, twins, response);
} else {
/*Codes_SRS_NODE_SERVICE_QUERY_16_007: [The `next` method shall call the `_callback` callback with a `null` error object, the results of the query and the response of the underlying transport if the request was successful.]*/
_callback(null, null, response);
}
}
});
}, (r, m) => {
return createResultWithIncomingMessage(r, m);
}, done);
}
}

1717
src/registry.ts Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

2892
src/service.json Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

70
src/service_receiver.ts Normal file
Просмотреть файл

@ -0,0 +1,70 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { Message, results, Callback, ErrorCallback, callbackToPromise, errorCallbackToPromise } from 'azure-iot-common';
import { ReceiverLink, AmqpMessage } from 'azure-iot-amqp-base';
import { EventEmitter } from 'events';
import { Client } from './client';
export class ServiceReceiver extends EventEmitter implements Client.ServiceReceiver {
private _receiver: ReceiverLink;
constructor(receiver: ReceiverLink) {
super();
this._receiver = receiver;
/*Codes_SRS_NODE_SERVICE_RECEIVER_16_001: [The constructor shall subscribe to the `message` event of the `ReceiverLink` object passed as argument.]*/
this._receiver.on('message', (amqpMessage) => {
/*Codes_SRS_NODE_SERVICE_RECEIVER_16_006: [The `ServiceReceiver` class shall convert any `AmqpMessage` received with the `message` event from the `ReceiverLink` object into `Message` objects and emit a `message` event with that newly created `Message` object for argument.]*/
this.emit('message', AmqpMessage.toMessage(amqpMessage));
});
/*Codes_SRS_NODE_SERVICE_RECEIVER_16_002: [The constructor shall subscribe to the `error` event of the `ReceiverLink` object passed as argument.]*/
this._receiver.on('error', (err) => {
/*Codes_SRS_NODE_SERVICE_RECEIVER_16_007: [Any error event received from the `ReceiverLink` object shall be forwarded as is.]*/
this.emit('error', err);
});
}
complete(message: Message, done: Callback<results.MessageCompleted>): void;
complete(message: Message): Promise<results.MessageCompleted>;
complete(message: Message, done?: Callback<results.MessageCompleted>): Promise<results.MessageCompleted> | void {
return callbackToPromise((_callback) => {
/*Codes_SRS_NODE_SERVICE_RECEIVER_16_003: [The `complete` method shall call the `complete` method on the `ReceiverLink` object and pass it the `AmqpMessage` stored within the `transportObj` property of the `Message` object as well as the `done` callback passed as argument.]*/
this._receiver.complete(message.transportObj, _callback);
}, done);
}
abandon(message: Message, done: Callback<results.MessageAbandoned>): void;
abandon(message: Message): Promise<results.MessageAbandoned>;
abandon(message: Message, done?: Callback<results.MessageAbandoned>): Promise<results.MessageAbandoned> | void {
/*Codes_SRS_NODE_SERVICE_RECEIVER_16_004: [The `abandon` method shall call the `abandon` method on the `ReceiverLink` object and pass it the `AmqpMessage` stored within the `transportObj` property of the `Message` object as well as the `done` callback passed as argument.]*/
return callbackToPromise((_callback) => {
this._receiver.abandon(message.transportObj, _callback);
}, done);
}
reject(message: Message, done: Callback<results.MessageRejected>): void;
reject(message: Message): Promise<results.MessageRejected>;
reject(message: Message, done?: Callback<results.MessageRejected>): Promise<results.MessageRejected> | void {
/*Codes_SRS_NODE_SERVICE_RECEIVER_16_005: [The `reject` method shall call the `reject` method on the `ReceiverLink` object and pass it the `AmqpMessage` stored within the `transportObj` property of the `Message` object as well as the `done` callback passed as argument.]*/
return callbackToPromise((_callback) => {
this._receiver.reject(message.transportObj, _callback);
}, done);
}
detach(callback: ErrorCallback): void;
detach(): Promise<void>;
detach(callback?: ErrorCallback): Promise<void> | void {
/*Codes_SRS_NODE_SERVICE_RECEIVER_16_008: [The `detach` method shall call the `detach` method on the `ReceiverLink` object and pass it its `callback` argument.]*/
return errorCallbackToPromise((_callback) => {
this._receiver.detach(_callback);
}, callback);
}
forceDetach(err?: Error): void {
/*Codes_SRS_NODE_SERVICE_RECEIVER_16_009: [The `forceDetach` method shall call the `forceDetach` method on the `ReceiverLink` object and pass it its `err` argument.]*/
this._receiver.forceDetach(err);
}
}

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

@ -0,0 +1,41 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { SharedAccessSignature } from 'azure-iot-common';
/**
* Creates a shared access signature token to authenticate a service connection with an Azure IoT hub.
*
* @param {string} host Hostname of the Azure IoT hub.
* @param {string} policy Name of the policy to use to connect to the Azure IoT hub (typically `ServiceConnect`, `iothubowner` etc. See [Control access to your Azure IoT hub]{@link https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security}.
* @param {string} key Symmetric key to use to create shared access signature tokens.
* @param {string} expiry Expiry time for the token that will be created.
*
* @throws {ReferenceError} If one of the parameters is falsy.
*
* @returns {SharedAccessSignature} A shared access signature to be used to connect with an Azure IoT hub.
*/
export function create(host: string, policy: string, key: string, expiry: string | number): SharedAccessSignature {
/*Codes_SRS_NODE_IOTHUB_SAS_05_003: [The create method shall return the result of calling azure-iot-common.SharedAccessSignature.create with following arguments:
resourceUri - host
keyName - policy
key - key
expiry - expiry]*/
return SharedAccessSignature.create(host, policy, key, expiry);
}
/**
* Parses a string in the format of a Shared Access Signature token and returns a {@link SharedAccessSignature}.
*
* @param source A shared access signature string.
* @returns {SharedAccessSignature} An object containing the different shared access signature properties extracted from the string given as a parameter
*
* @throws {FormatError} If the string cannot be parsed or is missing required parameters.
*/
export function parse(source: string): SharedAccessSignature {
/*Codes_SRS_NODE_IOTHUB_SAS_05_001: [The parse method shall return the result of calling azure-iot-common.SharedAccessSignature.parse.]*/
/*Codes_SRS_NODE_IOTHUB_SAS_05_002: [It shall throw ArgumentError if any of 'sr', 'sig', 'skn' or 'se' fields are not found in the source argument.]*/
return SharedAccessSignature.parse(source, ['sr', 'sig', 'skn', 'se']);
}

235
src/twin.ts Normal file
Просмотреть файл

@ -0,0 +1,235 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
import { errors, tripleValueCallbackToPromise } from 'azure-iot-common';
import * as _ from 'lodash';
import { DeviceIdentity } from './device';
import { Registry } from './registry';
import { IncomingMessageCallback, ResultWithIncomingMessage, createResultWithIncomingMessage } from './interfaces';
/**
* @private
*/
export interface TwinData {
deviceId: string;
moduleId?: string;
etag: string;
tags: { [key: string]: any };
properties: {
/**
* Reported properties: those are written to by the device and read by the service.
*/
reported: { [key: string]: any };
/**
* Desired properties: those are written to by the service and read by the device.
*/
desired: { [key: string]: any };
};
}
/**
* A Device Twin is document describing the state of a device that is stored by an Azure IoT hub and is available even if the device is offline.
* It is built around 3 sections:
* - Tags: key/value pairs only accessible from the service side
* - Desired Properties: updated by a service and received by the device
* - Reported Properties: updated by the device and received by the service.
*
* Note that although it is a possibility, desired and reported properties do not have to match
* and that the logic to sync these two collections, if necessary, is left to the user of the SDK.
*
* For more information see {@link https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-device-twins|Understanding Device Twins}.
*
* The recommended way to obtain a {@link azure-iothub.Twin} for a specific device is to use the {@link azure-iothub.Registry.getTwin} method.
*/
export class Twin implements TwinData {
/**
* Unique identifier of the device identity associated with the twin, as it exists in the device identity registry.
*/
deviceId: string;
/**
* Module identifier for the module associated with the twin, as it exists in the device identity registry.
*/
moduleId?: string;
/**
* Tag used in optimistic concurrency to avoid multiple parallel editions of the device twin.
*/
etag: string;
/**
* Model Id of the device identity associated with the twin, as it exists in the device identity registry.
*/
modelId?: string;
/**
* Collection of key/value pairs that is available only on the service side and can be used in queries to find specific devices.
*/
tags: {
[key: string]: string;
};
/**
* The desired and reported properties dictionaries (respectively in `properties.desired` and `properties.reported`).
*/
properties: {
/**
* Reported properties: those are written to by the device and read by the service.
*/
reported: { [key: string]: any };
/**
* Desired properties: those are written to by the service and read by the device.
*/
desired: { [key: string]: any };
};
private _registry: Registry;
/**
* Instantiates a new {@link azure-iothub.Twin}. The recommended way to get a new {@link azure-iothub.Twin} object is to use the {@link azure-iothub.Registry.getTwin} method.
*
* @constructor
* @param {string|Object} device A device identifier string or an object describing the device. If an Object,
* it must contain a deviceId property.
* @param {Registry} registryClient The HTTP registry client used to execute REST API calls.
*/
constructor(device: DeviceIdentity | string, registryClient: Registry) {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_002: [The `Twin(device, registryClient)` constructor shall throw a `ReferenceError` if `device` is falsy.]*/
if (device === null || device === undefined || device === '') throw new ReferenceError('\'device\' cannot be \'' + device + '\'');
/*Codes_SRS_NODE_IOTHUB_TWIN_16_003: [The `Twin(device, registryClient)` constructor shall throw a `ReferenceError` if `registryClient` is falsy.]*/
if (!registryClient) throw new ReferenceError('\'registryClient\' cannot be \'' + registryClient + '\'');
if (typeof (device) === 'string') {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_001: [The `Twin(device, registryClient)` constructor shall initialize an empty instance of a `Twin` object and set the `deviceId` base property to the `device` argument if it is a `string`.]*/
this.deviceId = device;
} else {
if (!device.deviceId) {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_007: [The `Twin(device, registryClient)` constructor shall throw an `ArgumentError` if `device` is an object and does not have a `deviceId` property.]*/
throw new errors.ArgumentError('\'device\' must have a deviceId property');
} else {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_006: [The `Twin(device, registryClient)` constructor shall initialize an empty instance of a `Twin` object and set the properties of the created object to the properties described in the `device` argument if it's an `object`.]*/
_.merge(this, device);
}
}
/*Codes_SRS_NODE_IOTHUB_TWIN_16_005: [The `Twin(device, registryClient)` constructor shall set the `Twin.etag` to `*`.]*/
this.etag = this.etag || '*';
this.tags = this.tags || {};
this.properties = _.assign({ desired: {} }, this.properties);
this._registry = registryClient;
}
/**
* @method module:azure-iothub.Twin.get
* @description Gets the latest version of this device twin from the IoT Hub service.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be called with three
* arguments: an Error object (can be null), a
* {@link module:azure-iothub.Twin|Twin}
* object representing the created device
* identity, and a transport-specific response
* object useful for logging or debugging.
* @returns {Promise<ResultWithIncomingMessage<Twin>> | void} Promise if no callback function was passed, void otherwise.
*/
get(done: IncomingMessageCallback<Twin>): void;
get(): Promise<ResultWithIncomingMessage<Twin>>;
get(done?: IncomingMessageCallback<Twin>): Promise<ResultWithIncomingMessage<Twin>> | void {
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_020: [If `this.moduleId` is falsy, the `get` method shall call the `getTwin` method of the `Registry` instance stored in `_registry` property with the following parameters:
- `this.deviceId`
- `done`]*/
/*Codes_SRS_NODE_IOTHUB_TWIN_18_001: [If `this.moduleId` is not falsy, the `get` method shall call the `getModuleTwin` method of the `Registry` instance stored in `_registry` property with the following parameters:
- `this.deviceId`
- `this.moduleId`
- `done`]*/
let get: any;
if (this.moduleId) {
get = (done) => this._registry.getModuleTwin(this.deviceId, this.moduleId, done);
} else {
get = (done) => this._registry.getTwin(this.deviceId, done);
}
get((err, result, response) => {
if (err) {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_022: [The method shall call the `_callback ` callback with an `Error` object if the request failed]*/
_callback(err);
} else {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_021: [The method shall copy properties, tags, and etag in the twin returned in the callback of the `Registry` method call into its parent object.]*/
this.properties = result.properties;
this.tags = result.tags;
this.etag = result.etag;
if (result.modelId) {
this.modelId = result.modelId;
}
/*Codes_SRS_NODE_IOTHUB_TWIN_16_023: [The method shall call the `_callback` callback with a `null` error object, its parent instance as a second argument and the transport `response` object as a third argument if the request succeeded.]*/
_callback(null, this, response);
}
});
}, (t, i) => {
return createResultWithIncomingMessage(t, i);
}, done);
}
/**
* @method module:azure-iothub.Twin.update
* @description Update the device twin with the patch provided as argument.
* @param {Object} patch Object containing the new values to apply to this device twin.
* @param {Function} [done] The optional function to call when the operation is
* complete. `done` will be called with three
* arguments: an Error object (can be null), a
* {@link module:azure-iothub.Twin|Twin}
* object representing the created device
* identity, and a transport-specific response
* object useful for logging or debugging.
* @returns {Promise<ResultWithIncomingMessage<Twin>> | void} Promise if no callback function was passed, void otherwise.
*/
update(patch: any, done: IncomingMessageCallback<Twin>): void;
update(patch: any): Promise<ResultWithIncomingMessage<Twin>>;
update(patch: any, done?: IncomingMessageCallback<Twin>): Promise<ResultWithIncomingMessage<Twin>> | void {
return tripleValueCallbackToPromise((_callback) => {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_019: [If `this.moduleId` is falsy, The `update` method shall call the `updateTwin` method of the `Registry` instance stored in `_registry` property with the following parameters:
- `this.deviceId`
- `patch`
- `this.etag`
- `done`]*/
/*Codes_SRS_NODE_IOTHUB_TWIN_18_002: [If `this.moduleId` is not falsy, the `update` method shall call the `updateModuleTwin` method of the `Registry` instance stored in `_registry` property with the following parameters:
- `this.deviceId`
- `this.moduleId`
- `patch`
- `this.etag`
- `done`]*/
let update: any;
if (this.moduleId) {
update = (done) => this._registry.updateModuleTwin(this.deviceId, this.moduleId, patch, this.etag, done);
} else {
update = (done) => this._registry.updateTwin(this.deviceId, patch, this.etag, done);
}
update((err, result, response) => {
if (err) {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_022: [The method shall call the `_callback` callback with an `Error` object if the request failed]*/
_callback(err);
} else {
/*Codes_SRS_NODE_IOTHUB_TWIN_16_021: [The method shall copy properties, tags, and etag in the twin returned in the callback of the `Registry` method call into its parent object.]*/
this.properties = result.properties;
this.tags = result.tags;
this.etag = result.etag;
/*Codes_SRS_NODE_IOTHUB_TWIN_16_023: [The method shall call the `_callback` callback with a `null` error object, its parent instance as a second argument and the transport `response` object as a third argument if the request succeeded.]*/
_callback(null, this, response);
}
});
}, (t, r) => {
return createResultWithIncomingMessage(t, r);
}, done);
}
/*Codes_SRS_NODE_IOTHUB_TWIN_16_015: [The `toJSON` method shall return a copy of the `Twin` object that doesn't contain the `_registry` private property.]*/
toJSON(): object {
// The registry object has a reference to https which has circular references, so we need to remove it from the JSON.
const serializable: any = {};
_.merge(serializable, this);
serializable._registry = undefined;
return serializable;
}
}

11
src/version.ts Normal file
Просмотреть файл

@ -0,0 +1,11 @@
/*! Copyright (c) Microsoft. All rights reserved.
*! Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
'use strict';
export const apiVersion = '2021-04-12';
export function versionQueryString(): string {
return '?api-version=' + apiVersion;
}

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

@ -0,0 +1,38 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const assert = require('chai').assert;
const errors = require('azure-iot-common').errors;
const translateError = require('../dist/amqp_service_errors.js').translateError;
describe('translateError', function () {
/*Tests_SRS_NODE_DEVICE_AMQP_SERVICE_ERRORS_16_001: [ `translateError` shall return an `DeviceMaximumQueueDepthExceededError` if the AMQP error condition is `amqp:resource-limit-exceeded`.] */
[
{ errorDescription: 'amqp:resource-limit-exceeded', errorMessage: 'Fake forbidden', expectedErrorType: errors.DeviceMaximumQueueDepthExceededError },
].forEach(function (testParams) {
it('returns an \'' + testParams.expectedErrorType.name + '\' if the amqp error description is \'' + testParams.errorDescription + '\'', function (){
const AMQPError = function AMQPError() {
Error.call(this);
};
const fake_error = new AMQPError();
fake_error.condition = testParams.errorDescription;
/*Tests_SRS_NODE_DEVICE_AMQP_ERRORS_16_010: [ `translateError` shall accept 2 argument:
*- A custom error message to give context to the user.
*- the AMQP error object itself]
*/
let err = translateError(new Error(testParams.errorMessage), fake_error);
assert.instanceOf(err, testParams.expectedErrorType);
/*Tests_SRS_NODE_DEVICE_AMQP_ERRORS_16_001: [Any error object returned by `translateError` shall inherit from the generic `Error` Javascript object and have 2 properties:
*- `amqpError` shall contain the error object returned by the AMQP layer.
*- `message` shall contain a human-readable error message]
*/
assert.equal(err.message, 'Error: ' + testParams.errorMessage);
assert.equal(err.amqpError, fake_error);
});
});
});

823
test/_amqp_test.js Normal file
Просмотреть файл

@ -0,0 +1,823 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const sinon = require('sinon');
const EventEmitter = require('events').EventEmitter;
const Amqp = require('../dist/amqp.js').Amqp;
const assert = require('chai').assert;
const SharedAccessSignature = require('azure-iot-common').SharedAccessSignature;
const errors = require('azure-iot-common').errors;
const results = require('azure-iot-common').results;
const Message = require('azure-iot-common').Message;
const AmqpMessage = require('azure-iot-amqp-base').AmqpMessage;
const fakeConfig = {
host: 'hub.host.name',
keyName: 'keyName',
sharedAccessSignature: 'SharedAccessSignature sr=a.hub.net&sig=1234&skn=keyName&se=1234'
};
const sasConfig = {
host: 'hub.host.name',
keyName: 'keyName',
sharedAccessSignature: SharedAccessSignature.create('uri', 'name', 'key', 123)
};
describe('Amqp', function () {
let fakeAmqpBase;
beforeEach(function () {
fakeAmqpBase = new EventEmitter();
fakeAmqpBase.connect = sinon.stub().callsArgWith(1, null, new results.Connected());
fakeAmqpBase.initializeCBS = sinon.stub().callsArg(0);
fakeAmqpBase.putToken = sinon.stub().callsArg(2);
fakeAmqpBase.disconnect = sinon.stub().callsArg(0);
fakeAmqpBase.setDisconnectHandler = function (eventHandler) {
this.on('disconnect', eventHandler);
};
});
afterEach(function () {
fakeAmqpBase = null;
sinon.restore();
});
it('automatically renews the SAS token before it expires', function (done) {
const clock = sinon.useFakeTimers();
try {
const renewingSasConfig = {
host: 'hub.host.name',
keyName: 'keyName',
sharedAccessSignature: SharedAccessSignature.create('uri', 'name', 'key', 1)
};
const amqp = new Amqp(renewingSasConfig, fakeAmqpBase);
amqp.connect(function () {
clock.tick(1800000); // 30 minutes. shouldn't have updated yet.
assert.equal(renewingSasConfig.sharedAccessSignature.se,3600);
clock.tick(1200000); // +20 => 50 minutes. Updater should have been invoked.
clock.restore();
assert.equal(renewingSasConfig.sharedAccessSignature.se,6300); // Should be 1 hour and 50 minutes from zero.
done();
});
} finally {
clock.restore();
}
});
it('automatically renews the AccessToken from the TokenCredential before it expires', async function () {
const tokenCredentialConfig = {
host: 'hub.host.name',
tokenCredential: {
getToken: sinon.stub().callsFake(() => Promise.resolve({
token: "fakeToken",
expiresOnTimestamp: Date.now() + 3600000 //One hour from now
}))
},
tokenScope: 'https://iothubs.azure.net/.default'
};
const clock = sinon.useFakeTimers();
try {
const amqp = new Amqp(tokenCredentialConfig, fakeAmqpBase);
await amqp.connect();
await clock.tickAsync(2400000 - 1); //+40m - 1ms (20m + 1ms before expiresOnTimestamp). Should not refresh.
assert(
tokenCredentialConfig.tokenCredential.getToken.calledOnce,
`Expected getToken() call count of 1, got call count of ${tokenCredentialConfig.tokenCredential.getToken.callCount}`
); //called once on initial connect
assert(
fakeAmqpBase.putToken.calledOnce,
`Expected putToken() call count of 1, got call count of ${fakeAmqpBase.putToken.callCount}`
); //called once on initial connect
await clock.tickAsync(1); //+1ms (20 minutes before expiresOnTimestamp). Should refresh since we are now at 2/3 of the expiration window.
assert(
tokenCredentialConfig.tokenCredential.getToken.calledTwice,
`Expected getToken() call count of 2, got call count of ${tokenCredentialConfig.tokenCredential.getToken.callCount}`
);
assert(
fakeAmqpBase.putToken.calledTwice,
`Expected putToken() call count of 2, got call count of ${fakeAmqpBase.putToken.callCount}`
);
await clock.tickAsync(2400000 - 1); ////+40m - 1ms (20m + 1ms before expiresOnTimestamp). Second refresh shouldn't happen yet.
assert(
tokenCredentialConfig.tokenCredential.getToken.calledTwice,
`Expected getToken() call count of 2, got call count of ${tokenCredentialConfig.tokenCredential.getToken.callCount}`
);
assert(
fakeAmqpBase.putToken.calledTwice,
`Expected putToken() call count of 2, got call count of ${fakeAmqpBase.putToken.callCount}`
);
await clock.tickAsync(1); ////+1ms (20 minutes before expiresOnTimestamp). Second refresh should happen.
assert(
tokenCredentialConfig.tokenCredential.getToken.calledThrice,
`Expected getToken() call count of 3, got call count of ${tokenCredentialConfig.tokenCredential.getToken.callCount}`
);
assert(
fakeAmqpBase.putToken.calledThrice,
`Expected putToken() call count of 3, got call count of ${fakeAmqpBase.putToken.callCount}`
);
assert(fakeAmqpBase.putToken.alwaysCalledWith('https://iothubs.azure.net/.default', "Bearer " + "fakeToken"), "putToken() was called with the incorrect arguments");
assert(tokenCredentialConfig.tokenCredential.getToken.alwaysCalledWithExactly('https://iothubs.azure.net/.default'), "getToken() was called with incorrect arguments");
await amqp.disconnect();
} finally {
clock.restore();
}
});
describe('#constructor', function () {
it('sets up \'disconnect\' event forwarding', function (done){
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.on('disconnect', function () {
done();
});
amqp.connect(function () {
fakeAmqpBase.emit('disconnect', new Error('fake'));
});
});
it('ignores AMQP errors received while disconnected', function () {
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.on('disconnect', function () {
assert.fail();
});
fakeAmqpBase.emit('disconnect', new Error('fake'));
});
it('disconnects the transport if an AMQP error is emitted while connecting', function (testCallback) {
fakeAmqpBase.connect = sinon.stub();
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.on('disconnect', function (err) {
assert.instanceOf(err, Error);
assert.isTrue(fakeAmqpBase.disconnect.calledOnce);
testCallback();
});
amqp.connect(function () {});
// blocked in connecting because the connect callback won't be called.
fakeAmqpBase.emit('disconnect', new Error('fake'));
});
it('disconnects the transport if an AMQP error is emitted while authenticating', function (testCallback) {
fakeAmqpBase.putToken = sinon.stub();
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.on('disconnect', function (err) {
assert.instanceOf(err, Error);
assert.isTrue(fakeAmqpBase.disconnect.calledOnce);
testCallback();
});
amqp.connect(function () {});
// blocked in connecting because the putToken callback won't be called.
fakeAmqpBase.emit('disconnect', new Error('fake'));
});
});
describe('#connect', function () {
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_019: [The `connect` method shall call the `connect` method of the base AMQP transport and translate its result to the caller into a transport-agnostic object.]*/
it('calls the base transport connect method', function (done) {
const amqp = new Amqp(sasConfig, fakeAmqpBase);
amqp.connect(function () {
assert.isTrue(fakeAmqpBase.connect.calledOnce);
amqp.disconnect(function () {
done();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_017: [All asynchronous instance methods shall call the `done` callback with a single parameter that is derived from the standard Javascript `Error` object if the operation failed.]*/
it('calls its callback with an error if the base transport connect method fails', function (done) {
const testError = new errors.NotConnectedError('fake error');
fakeAmqpBase.connect = sinon.stub().callsArgWith(1, testError);
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function (err) {
assert.instanceOf(err, Error);
amqp.disconnect(function () {
done();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_06_001: [`initializeCBS` shall be invoked.]*/
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_06_002: [If `initializeCBS` is not successful then the client will remain disconnected and the callback, if provided, will be invoked with an error object.]*/
it('Invokes initializeCBS - initialize fails and disconnects', function (testCallback) {
const testError = new errors.InternalServerError('fake error');
fakeAmqpBase.initializeCBS = sinon.stub().callsArgWith(0, testError);
const transport = new Amqp(sasConfig, fakeAmqpBase);
transport.connect(function (err) {
assert(fakeAmqpBase.initializeCBS.calledOnce);
assert(fakeAmqpBase.disconnect.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_06_003: [If `initializeCBS` is successful, `putToken` shall be invoked with the first parameter audience, created from the sr of the sas signature, the next parameter of the actual sas, and a callback.]*/
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_06_004: [If `putToken` is not successful then the client will remain disconnected and the callback, if provided, will be invoked with an error object.]*/
it('Invokes putToken - putToken fails and disconnects', function (testCallback) {
const testError = new errors.NotConnectedError('fake error');
fakeAmqpBase.putToken = sinon.stub().callsArgWith(2, testError);
const transport = new Amqp(sasConfig, fakeAmqpBase);
transport.connect(function (err) {
assert(fakeAmqpBase.putToken.calledOnce);
assert(fakeAmqpBase.putToken.calledWith('uri',sasConfig.sharedAccessSignature.toString()));
assert(fakeAmqpBase.disconnect.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
it('Invokes getToken - getToken fails and disconnects', async function () {
const tokenCredentialConfig = {
host: 'hub.host.name',
tokenCredential: {
getToken: sinon.stub().rejects()
},
tokenScope: 'https://iothubs.azure.net/.default'
};
const amqp = new Amqp(tokenCredentialConfig, fakeAmqpBase);
try {
await amqp.connect();
} catch (err) {
assert(
tokenCredentialConfig.tokenCredential.getToken.calledOnceWithExactly('https://iothubs.azure.net/.default'),
"getToken() was not called exactly once with the correct args"
);
assert(
fakeAmqpBase.disconnect.calledOnce,
`Expected disconnect() call count of 1, got call count of ${fakeAmqpBase.disconnect.callCount}`
);
return;
}
assert.fail("Expected connect() to fail, but it succeeded");
});
it('calls its callback immediately if it is already connected', function (testCallback) {
const transport = new Amqp(sasConfig, fakeAmqpBase);
transport.connect(function () {
assert.isTrue(fakeAmqpBase.connect.calledOnce);
assert.isTrue(fakeAmqpBase.initializeCBS.calledOnce);
assert.isTrue(fakeAmqpBase.putToken.calledOnce);
transport.connect(function () {
assert.isTrue(fakeAmqpBase.connect.calledOnce);
assert.isTrue(fakeAmqpBase.initializeCBS.calledOnce);
assert.isTrue(fakeAmqpBase.putToken.calledOnce);
transport.disconnect(function () {
testCallback();
});
});
});
});
it('gets the token from the TokenCredential object using the correct token scope and passes it to putToken if a TokenCredential is in the config', async function () {
const tokenCredentialConfig = {
host: 'hub.host.name',
tokenCredential: {
getToken: sinon.stub().callsFake(() => Promise.resolve({
token: "fakeToken",
expiresOnTimestamp: Date.now() + 3600000 //One hour from now
}))
},
tokenScope: 'https://iothubs.azure.net/.default'
};
const transport = new Amqp(tokenCredentialConfig, fakeAmqpBase);
await transport.connect();
assert(
tokenCredentialConfig.tokenCredential.getToken.calledOnceWithExactly('https://iothubs.azure.net/.default'),
"The TokenCredential's getToken() method was not called exactly once with the correct args"
);
assert(
fakeAmqpBase.putToken.calledOnceWith('https://iothubs.azure.net/.default', "Bearer " + "fakeToken"),
"The base AMQP's putToken() method was not called exactly once with the correct args"
);
await transport.disconnect();
});
});
describe('#disconnect', function () {
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_020: [** The `disconnect` method shall call the `disconnect` method of the base AMQP transport and translate its result to the caller into a transport-agnostic object.]*/
it('calls the base transport disconnect method if the transport is connected', function (testCallback) {
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.disconnect(function () {
assert.isTrue(fakeAmqpBase.disconnect.calledOnce);
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_020: [** The `disconnect` method shall call the `disconnect` method of the base AMQP transport and translate its result to the caller into a transport-agnostic object.]*/
it('calls the base transport disconnect method if the transport is connecting', function (testCallback) {
fakeAmqpBase.connect = sinon.stub();
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {});
// blocked in connecting because the connect callback won't be called.
amqp.disconnect(function () {
assert.isTrue(fakeAmqpBase.disconnect.calledOnce);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_018: [All asynchronous instance methods shall call the `done` callback with either no arguments or a first null argument and a second argument that is the result of the operation if the operation succeeded.]*/
it('calls the base transport disconnect method if the transport is authenticating', function (testCallback) {
fakeAmqpBase.putToken = sinon.stub();
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {});
// blocked in authenticating because the putToken callback won't be called.
amqp.disconnect(function () {
assert.isTrue(fakeAmqpBase.disconnect.calledOnce);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_018: [All asynchronous instance methods shall call the `done` callback with either no arguments or a first null argument and a second argument that is the result of the operation if the operation succeeded.]*/
it('calls the base transport disconnect method with renewable sas config', function (testCallback) {
const amqp = new Amqp(sasConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.disconnect(function () {
assert.isTrue(fakeAmqpBase.disconnect.calledOnce);
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_017: [All asynchronous instance methods shall call the `done` callback with a single parameter that is derived from the standard Javascript `Error` object if the operation failed.]*/
it('calls its callback with an error if the base transport disconnect method fails', function (testCallback) {
fakeAmqpBase.disconnect = sinon.stub().callsArgWith(0, new Error('fake'));
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.disconnect(function (err) {
assert.isTrue(fakeAmqpBase.disconnect.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
});
it('calls its callback directly if already disconnected', function (testCallback) {
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.disconnect(function () {
assert.isTrue(fakeAmqpBase.disconnect.notCalled);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_021: [The `disconnect` method shall detach the C2D messaging link if it is attached.]*/
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_022: [The `disconnect` method shall detach the C2D feedback receiver link if it is attached.]*/
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_023: [The `disconnect` method shall detach the file notification receiver link if it is attached.]*/
it('detaches links', function (testCallback) {
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
fakeSender.detach = sinon.stub().callsArg(0);
const fakeFileNotificationReceiver = new EventEmitter();
fakeFileNotificationReceiver.detach = sinon.stub().callsArg(0);
const fakeFeedbackReceiver = new EventEmitter();
fakeFeedbackReceiver.detach = sinon.stub().callsArg(0);
fakeAmqpBase.attachSenderLink = sinon.stub().callsArgWith(2, null, fakeSender);
fakeAmqpBase.attachReceiverLink = sinon.stub().onFirstCall().callsArgWith(2, null, fakeFileNotificationReceiver)
.onSecondCall().callsArgWith(2, null, fakeFeedbackReceiver);
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.send('fakeDeviceId', new Message('fake'), function () {
amqp.getFileNotificationReceiver(function () {
amqp.getFeedbackReceiver(function () {
amqp.disconnect(function () {
assert.isTrue(fakeSender.detach.calledOnce);
assert.isTrue(fakeFeedbackReceiver.detach.calledOnce);
assert.isTrue(fakeFileNotificationReceiver.detach.calledOnce);
assert.isTrue(fakeAmqpBase.disconnect.calledOnce);
testCallback();
});
});
});
});
});
});
it('forcefully detaches the links if it receives an error from the lower AMQP layer', function (testCallback) {
const fakeError = new Error('fake');
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
fakeSender.forceDetach = sinon.stub();
const fakeFileNotificationReceiver = new EventEmitter();
fakeFileNotificationReceiver.forceDetach = sinon.stub();
const fakeFeedbackReceiver = new EventEmitter();
fakeFeedbackReceiver.forceDetach = sinon.stub();
fakeAmqpBase.attachSenderLink = sinon.stub().callsArgWith(2, null, fakeSender);
fakeAmqpBase.attachReceiverLink = sinon.stub().onFirstCall().callsArgWith(2, null, fakeFileNotificationReceiver)
.onSecondCall().callsArgWith(2, null, fakeFeedbackReceiver);
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.on('disconnect', function (err) {
assert.strictEqual(err, fakeError);
assert.isTrue(fakeSender.forceDetach.calledOnce);
assert.isTrue(fakeSender.forceDetach.calledWith(fakeError));
assert.isTrue(fakeFeedbackReceiver.forceDetach.calledOnce);
assert.isTrue(fakeFeedbackReceiver.forceDetach.calledWith(fakeError));
assert.isTrue(fakeFileNotificationReceiver.forceDetach.calledOnce);
assert.isTrue(fakeFileNotificationReceiver.forceDetach.calledWith(fakeError));
assert.isTrue(fakeAmqpBase.disconnect.calledOnce);
testCallback();
});
amqp.connect(function () {
amqp.send('fakeDeviceId', new Message('fake'), function () {
amqp.getFileNotificationReceiver(function () {
amqp.getFeedbackReceiver(function () {
fakeAmqpBase.emit('disconnect', fakeError);
});
});
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_024: [Any error generated by detaching a link should be passed as the single argument of the callback of the `disconnect` method.]*/
it('calls its callback with an error if detaching the sender link fails', function (testCallback) {
const fakeError = new Error('fake');
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
fakeSender.detach = sinon.stub().callsArgWith(0, fakeError);
fakeAmqpBase.attachSenderLink = sinon.stub().callsArgWith(2, null, fakeSender);
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.send('fakeDeviceId', new Message('fake'), function () {
amqp.disconnect(function (err) {
assert.instanceOf(err, Error);
testCallback();
});
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_024: [Any error generated by detaching a link should be passed as the single argument of the callback of the `disconnect` method.]*/
it('calls its callback with an error if detaching the feedback receiver fails', function (testCallback) {
const fakeError = new Error('fake');
const fakeReceiver = new EventEmitter();
fakeReceiver.detach = sinon.stub().callsArgWith(0, fakeError);
fakeAmqpBase.attachReceiverLink = sinon.stub().callsArgWith(2, null, fakeReceiver);
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.getFeedbackReceiver(function () {
amqp.disconnect(function (err) {
assert.instanceOf(err, Error);
testCallback();
});
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_024: [Any error generated by detaching a link should be passed as the single argument of the callback of the `disconnect` method.]*/
it('calls its callback with an error if detaching the file notification receiver fails', function (testCallback) {
const fakeError = new Error('fake');
const fakeReceiver = new EventEmitter();
fakeReceiver.detach = sinon.stub().callsArgWith(0, fakeError);
fakeAmqpBase.attachReceiverLink = sinon.stub().callsArgWith(2, null, fakeReceiver);
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.getFileNotificationReceiver(function () {
amqp.disconnect(function (err) {
assert.instanceOf(err, Error);
testCallback();
});
});
});
});
});
describe('#getFeedbackReceiver', function () {
it('connects the transport if necessary', function (testCallback) {
fakeAmqpBase.attachReceiverLink = function (endpoint, linkOptions, callback) {
assert.equal(endpoint, '/messages/serviceBound/feedback');
callback(null, new EventEmitter());
};
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.getFeedbackReceiver(function () {
assert.isTrue(fakeAmqpBase.connect.calledOnce);
assert.isTrue(fakeAmqpBase.initializeCBS.calledOnce);
assert.isTrue(fakeAmqpBase.putToken.calledOnce);
testCallback();
});
});
it('calls its callback with an error if connecting the transport fails', function (testCallback) {
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.connect = sinon.stub().callsArgWith(1, new Error('fakeError'));
amqp.getFeedbackReceiver(function (err) {
assert.isTrue(fakeAmqpBase.connect.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
it('Reuses the existing receiver if it is already attached', function (testCallback) {
fakeAmqpBase.attachReceiverLink = function (endpoint, linkOptions, callback) {
assert.equal(endpoint, '/messages/serviceBound/feedback');
callback(null, new EventEmitter());
};
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.getFeedbackReceiver(function (err, receiver) {
const recv1 = receiver;
amqp.getFeedbackReceiver(function (err, recv2) {
assert.strictEqual(recv1, recv2);
testCallback();
});
});
});
});
it('calls its callback with an error if the link fails to attach', function (testCallback) {
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.attachReceiverLink = sinon.stub().callsArgWith(2, new Error('fake error'));
amqp.connect(function () {
amqp.getFeedbackReceiver(function (err) {
assert.isTrue(fakeAmqpBase.attachReceiverLink.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_013: [The `getFeedbackReceiver` method shall request an `AmqpReceiver` object from the base AMQP transport for the `/messages/serviceBound/feedback` endpoint.]*/
it('requests an AmqpReceiver from the feedback endpoint', function (testCallback) {
fakeAmqpBase.attachReceiverLink = function (endpoint, linkOptions, callback) {
assert.equal(endpoint, '/messages/serviceBound/feedback');
callback(null, new EventEmitter());
};
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.getFeedbackReceiver(testCallback);
});
});
});
describe('#getFileNotificationReceiver', function () {
it('connects the transport if necessary', function (testCallback) {
fakeAmqpBase.attachReceiverLink = function (endpoint, linkOptions, callback) {
assert.equal(endpoint, '/messages/serviceBound/filenotifications');
callback(null, new EventEmitter());
};
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.getFileNotificationReceiver(function () {
assert.isTrue(fakeAmqpBase.connect.calledOnce);
assert.isTrue(fakeAmqpBase.initializeCBS.calledOnce);
assert.isTrue(fakeAmqpBase.putToken.calledOnce);
testCallback();
});
});
it('calls its callback with an error if connecting the transport fails', function (testCallback) {
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.connect = sinon.stub().callsArgWith(1, new Error('fakeError'));
amqp.getFileNotificationReceiver(function (err) {
assert.isTrue(fakeAmqpBase.connect.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_016: [The `getFileNotificationReceiver` method shall request an `AmqpReceiver` object from the base AMQP transport for the `/messages/serviceBound/filenotifications` endpoint.]*/
it('requests an AmqpReceiver from the file notification endpoint', function (testCallback) {
fakeAmqpBase.attachReceiverLink = function (endpoint, linkOptions, callback) {
assert.equal(endpoint, '/messages/serviceBound/filenotifications');
callback(null, new EventEmitter());
};
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.getFileNotificationReceiver(testCallback);
});
});
it('Reuses the existing receiver if it is already attached', function (testCallback) {
fakeAmqpBase.attachReceiverLink = function (endpoint, linkOptions, callback) {
assert.equal(endpoint, '/messages/serviceBound/filenotifications');
callback(null, new EventEmitter());
};
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.getFileNotificationReceiver(function (err, receiver) {
const recv1 = receiver;
amqp.getFileNotificationReceiver(function (err, recv2) {
assert.strictEqual(recv1, recv2);
testCallback();
});
});
});
});
it('calls its callback with an error if the link fails to attach', function (testCallback) {
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.attachReceiverLink = sinon.stub().callsArgWith(2, new Error('fake error'));
amqp.connect(function () {
amqp.getFileNotificationReceiver(function (err) {
assert.isTrue(fakeAmqpBase.attachReceiverLink.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
});
});
[
{
functionUnderTest: 'send',
invokeFunctionUnderTest: function (amqp, msg, callback) { amqp.send('fakeDeviceId', msg, callback); },
expectedDestination: '/devices/fakeDeviceId/messages/devicebound'
}
].forEach(function (testConfig) {
describe('#' + testConfig.functionUnderTest, function () {
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_025: [The `send` method shall connect and authenticate the transport if it is disconnected.]*/
it('connects the transport if necessary', function (testCallback) {
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.attachSenderLink = sinon.stub().callsArgWith(2, null, fakeSender);
testConfig.invokeFunctionUnderTest(amqp, new Message('foo'), function () {
assert.isTrue(fakeAmqpBase.connect.calledOnce);
assert.isTrue(fakeAmqpBase.initializeCBS.calledOnce);
assert.isTrue(fakeAmqpBase.putToken.calledOnce);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_026: [The `send` method shall call its callback with an error if connecting and/or authenticating the transport fails.]*/
it('calls its callback with an error if connecting the transport fails', function (testCallback) {
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.connect = sinon.stub().callsArgWith(1, new Error('fakeError'));
testConfig.invokeFunctionUnderTest(amqp, new Message('foo'), function (err) {
assert.isTrue(fakeAmqpBase.connect.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_027: [The `send` method shall attach the C2D link if necessary.]*/
it('attaches the link if necessary', function (testCallback) {
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.attachSenderLink = sinon.stub().callsArgWith(2, fakeSender);
amqp.connect(function () {
testConfig.invokeFunctionUnderTest(amqp, new Message('foo'), function () {
assert.isTrue(fakeAmqpBase.attachSenderLink.calledOnce);
assert.isTrue(fakeAmqpBase.attachSenderLink.calledWith('/messages/devicebound'));
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_029: [The `send` method shall call its callback with an error if it fails to attach the C2D link.]*/
it('calls its callback with an error if the link fails to attach', function (testCallback) {
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.attachSenderLink = sinon.stub().callsArgWith(2, new Error('fake error'));
amqp.connect(function () {
testConfig.invokeFunctionUnderTest(amqp, new Message('foo'), function (err) {
assert.isTrue(fakeAmqpBase.attachSenderLink.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_030: [The `send` method shall call the `send` method of the C2D link and pass it the Amqp request that it created.]*/
it('calls send on the c2d link object', function (testCallback) {
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.attachSenderLink = sinon.stub().callsArgWith(2, null, fakeSender);
amqp.connect(function () {
testConfig.invokeFunctionUnderTest(amqp, new Message('foo'), function () {
assert.isTrue(fakeSender.send.calledOnce);
assert.instanceOf(fakeSender.send.firstCall.args[0], AmqpMessage);
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_003: [The message generated by the `send` method should have its “to” field set to "/devices/(uriEncode<deviceId>)/messages/devicebound".]*/
assert.strictEqual(fakeSender.send.firstCall.args[0].to, testConfig.expectedDestination);
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_017: [All asynchronous instance methods shall call the `done` callback with a single parameter that is derived from the standard Javascript `Error` object if the operation failed.]*/
it('calls its callback with an error if the link fails to send the message', function (testCallback) {
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, new Error('fake'));
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.attachSenderLink = sinon.stub().callsArgWith(2, null, fakeSender);
amqp.connect(function () {
testConfig.invokeFunctionUnderTest(amqp, new Message('foo'), function (err) {
assert.isTrue(fakeSender.send.calledOnce);
assert.instanceOf(err, Error);
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_028: [The `send` method shall reuse the C2D link if it is already attached.]*/
it('Reuses the c2d link object', function (testCallback) {
const fakeSender = new EventEmitter();
fakeSender.send = sinon.stub().callsArgWith(1, null, new results.MessageEnqueued());
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
fakeAmqpBase.attachSenderLink = sinon.stub().callsArgWith(2, null, fakeSender);
amqp.connect(function () {
testConfig.invokeFunctionUnderTest(amqp, new Message('test1'), function () {
assert.isTrue(fakeAmqpBase.attachSenderLink.calledOnce);
assert.isTrue(fakeSender.send.calledOnce);
testConfig.invokeFunctionUnderTest(amqp, new Message('test2'), function () {
assert.isTrue(fakeAmqpBase.attachSenderLink.calledOnce);
assert.isTrue(fakeSender.send.calledTwice);
testCallback();
});
});
});
});
});
});
describe('updateSharedAccessSignature', function () {
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_039: [The `updateSharedAccessSignature` shall throw a `ReferenceError` if the `sharedAccessSignature` argument is falsy.]*/
[undefined, null, ''].forEach(function (badSas) {
it('throws if sharedAccessSignature is \'' + badSas + '\'', function () {
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
assert.throws(function () {
amqp.updateSharedAccessSignature(badSas, function () {});
}, ReferenceError);
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_031: [The `updateSharedAccessSignature` shall trigger a `putToken` call on the base transport if it is connected.]*/
it('calls putToken on the lower layer amqp object', function (testCallback) {
const fakeSas = 'SharedAccessSignature sr=a.hub.net&sig=1234&skn=keyName&se=4567';
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
amqp.updateSharedAccessSignature(fakeSas, function () {
assert.isTrue(fakeAmqpBase.putToken.calledTwice);
assert.strictEqual(fakeAmqpBase.putToken.secondCall.args[1], fakeSas);
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_017: [All asynchronous instance methods shall call the `done` callback with a single parameter that is derived from the standard Javascript `Error` object if the operation failed.]*/
it('calls its callback with an error if the putToken operation fails', function (testCallback) {
const fakeSas = 'SharedAccessSignature sr=a.hub.net&sig=1234&skn=keyName&se=4567';
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.connect(function () {
fakeAmqpBase.putToken = sinon.stub().callsArgWith(2, new Error('fake'));
amqp.updateSharedAccessSignature(fakeSas, function (err) {
assert.instanceOf(err, Error);
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_16_032: [The `updateSharedAccessSignature` shall not establish a connection if the transport is disconnected, but should use the new shared access signature on the next manually initiated connection attempt. **]*/
it('does not connect the transport if it is disconnected', function (testCallback) {
const fakeSas = 'SharedAccessSignature sr=a.hub.net&sig=1234&skn=keyName&se=4567';
const amqp = new Amqp(fakeConfig, fakeAmqpBase);
amqp.updateSharedAccessSignature(fakeSas, function () {
assert.isTrue(fakeAmqpBase.connect.notCalled);
testCallback();
});
});
});
});

44
test/_amqp_ws_test.js Normal file
Просмотреть файл

@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const Amqp = require('../dist/amqp.js').Amqp;
const AmqpWs = require('../dist/amqp_ws.js').AmqpWs;
const assert = require('chai').assert;
const results = require('azure-iot-common').results;
const sinon = require('sinon');
describe('AmqpWs', function () {
describe('#constructor', function () {
/*Tests_SRS_NODE_IOTHUB_SERVICE_AMQP_WS_16_002: [`AmqpWs` should inherit from `Amqp`.]*/
it('inherits from `Amqp`', function () {
const amqpWs = new AmqpWs({
host: 'host',
keyName: 'keyName',
sharedAccessSignature: 'sas'
});
assert.instanceOf(amqpWs, Amqp);
});
});
describe('#connect', function () {
it('calls the connect method on the base AMQP object with the correct URL', function () {
const testConfig = {
host: 'host',
keyName: 'keyName',
sharedAccessSignature: 'sas'
};
const amqpWs = new AmqpWs(testConfig);
amqpWs._amqp.connect = sinon.stub().callsArgWith(1, null, new results.Connected());
amqpWs.initializeCBS = sinon.stub().callsArg(0);
amqpWs.putToken = sinon.stub().callsArg(2);
amqpWs.disconnect = sinon.stub().callsArg(0);
amqpWs.connect(function (){});
assert.strictEqual(amqpWs._amqp.connect.args[0][0].uri.indexOf('wss://'), 0);
assert(amqpWs._amqp.connect.args[0][0].uri.indexOf('$iothub/websocket') > 0);
});
});
});

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

@ -0,0 +1,131 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const assert = require('chai').assert;
const Client = require('../dist/client.js').Client;
const errors = require('azure-iot-common').errors;
const Message = require('azure-iot-common').Message;
const debug = require('debug')('azure-iothub:_client_common_testrun');
function transportSpecificTests(opts) {
describe('Client', function () {
let testSubject;
const deviceId = 'testDevice-node-' + Math.random();
const moduleId = 'testModule-' + Math.random();
before('prepare test subject', function (done) {
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_008: [The open method shall open a connection to the IoT Hub that was identified when the Client object was created (e.g., in Client.fromConnectionString).]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_009: [When the open method completes, the callback function (indicated by the done argument) shall be invoked with the following arguments:
err - standard JavaScript Error object (or subclass)]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_010: [The argument err passed to the callback done shall be null if the protocol operation was successful.]*/
opts.registry.create({ deviceId: deviceId, status: "enabled" }, function (err) {
debug('create returned ' + (err ? err : 'success'));
if (err) {
done(err);
} else {
const module = {
deviceId: deviceId,
moduleId: moduleId
};
opts.registry.addModule(module, function (err) {
debug('addModule returned ' + (err ? err : 'success'));
if (err) {
done(err);
} else {
testSubject = Client.fromConnectionString(opts.connectionString, opts.transport);
testSubject.open(function (err) {
done(err);
});
}
});
}
});
});
after('close the connection', function (done) {
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_021: [The close method shall close the connection.]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_022: [When the close method completes, the callback function (indicated by the done argument) shall be invoked with the following arguments:
err - standard JavaScript Error object (or subclass)]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_023: [The argument err passed to the callback done shall be null if the protocol operation was successful.]*/
testSubject.close(function (closeErr) {
opts.registry.delete(deviceId, function (registryErr) {
if (closeErr || registryErr) {
const aggregateError = new Error('failed to tear down the environment');
aggregateError.closeError = closeErr;
aggregateError.registryError = registryErr;
done(aggregateError);
} else {
done();
}
});
});
});
describe('#send', function () {
function createTestMessage() {
const msg = new Message('msg');
msg.expiryTimeUtc = Date.now() + 5000; // Expire 5s from now, to reduce the chance of us hitting the 50-message limit on the IoT Hub
return msg;
}
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_016: [When the send method completes, the callback function (indicated by the done argument) shall be invoked with the following arguments:
err - standard JavaScript Error object (or subclass)
response - an implementation-specific response object returned by the underlying protocol, useful for logging and troubleshooting]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_017: [The argument err passed to the callback done shall be null if the protocol operation was successful.]*/
it('sends the message', function (done) {
testSubject.send(deviceId, createTestMessage(), function (err, state) {
if (!err) {
assert.equal(state.constructor.name, "MessageEnqueued");
}
done(err);
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_019: [If the deviceId has not been registered with the IoT Hub, send shall return an instance of DeviceNotFoundError.]*/
it('returns DeviceNotFoundError when sending to an unregistered deviceId', function (done) {
// DeviceNotFound returned because
// 1) amqp_simulated has special-case code for devices with 'no-device' in the name.
// 2) real hubs don't have this device.
const unregisteredDeviceId = 'no-device' + Math.random();
testSubject.send(unregisteredDeviceId, new Message('msg'), function (err) {
assert.instanceOf(err, errors.DeviceNotFoundError);
done();
});
});
});
describe('#getFeedbackReceiver', function () {
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_027: [When the `getFeedbackReceiver` method completes, the callback function (indicated by the `done` argument) shall be invoked with the following arguments:
- `err` - standard JavaScript `Error` object (or subclass): `null` if the operation was successful
- `receiver` - an `AmqpReceiver` instance: `undefined` if the operation failed]*/
it('calls the `done` callback with a null error object and an AmqpReceiver object if the operation succeeds', function (done) {
testSubject.getFeedbackReceiver(function (err) {
if (err) done(err);
else {
done();
}
});
});
});
describe('#getFileNotificationReceiver', function () {
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_001: [When the `getFileNotificationReceiver` method completes, the callback function (indicated by the `done` argument) shall be invoked with the following arguments:
- `err` - standard JavaScript `Error` object (or subclass): `null` if the operation was successful
- `receiver` - an `AmqpReceiver` instance: `undefined` if the operation failed]*/
it('calls the `done` callback with a null error object and an AmqpReceiver object if the operation succeeds', function (done) {
testSubject.getFileNotificationReceiver(function (err) {
if (err) done(err);
else {
done();
}
});
});
});
});
}
// eslint-disable-next-line mocha/no-exports
module.exports = transportSpecificTests;

630
test/_client_test.js Normal file
Просмотреть файл

@ -0,0 +1,630 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const assert = require('chai').assert;
const sinon = require('sinon');
const EventEmitter = require('events').EventEmitter;
const Amqp = require('../dist/amqp.js').Amqp;
const Client = require('../dist/client.js').Client;
const Message = require('azure-iot-common').Message;
const errors = require('azure-iot-common').errors;
const SimulatedAmqp = require('./amqp_simulated.js');
const transportSpecificTests = require('./_client_common_testrun.js');
describe('Client', function () {
describe('#constructor', function () {
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_001: [The Client constructor shall throw ReferenceError if the transport argument is falsy.]*/
it('throws when transport is falsy', function () {
assert.throws(function () {
return new Client();
}, ReferenceError, 'transport is \'undefined\'');
});
});
describe('#fromConnectionString', function () {
const connStr = 'HostName=a.b.c;SharedAccessKeyName=name;SharedAccessKey=key';
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_002: [The fromConnectionString method shall throw ReferenceError if the connStr argument is falsy.]*/
it('throws when value is falsy', function () {
assert.throws(function () {
return Client.fromConnectionString();
}, ReferenceError, 'connStr is \'undefined\'');
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_017: [The `fromConnectionString` method shall use the default Transport (Amqp) if the `Transport` optional argument is falsy.]*/
it('creates an instance of the default transport', function () {
const client = Client.fromConnectionString(connStr);
assert.instanceOf(client._transport, Amqp);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_016: [The `fromConnectionString` method shall use the `Transport` constructor passed as argument to instantiate a transport object if it's not falsy.]*/
it('uses the transport given as argument', function () {
const FakeTransport = function (config) {
assert.isOk(config);
};
const client = Client.fromConnectionString(connStr, FakeTransport);
assert.instanceOf(client._transport, FakeTransport);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_004: [The fromConnectionString method shall return a new instance of the Client object, as by a call to new Client(transport).]*/
it('returns an instance of Client', function () {
const client = Client.fromConnectionString(connStr);
assert.instanceOf(client, Client);
assert.isOk(client._restApiClient);
});
});
describe('#fromSharedAccessSignature', function () {
const token = 'SharedAccessSignature sr=hubName.azure-devices.net&sig=signature&skn=keyname&se=expiry';
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_005: [The fromSharedAccessSignature method shall throw ReferenceError if the sharedAccessSignature argument is falsy.]*/
it('throws when value is falsy', function () {
assert.throws(function () {
return Client.fromSharedAccessSignature();
}, ReferenceError, 'sharedAccessSignature is \'undefined\'');
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_018: [The `fromSharedAccessSignature` method shall create a new transport instance and pass it a config object formed from the connection string given as argument.]*/
it('correctly populates the config structure', function () {
const client = Client.fromSharedAccessSignature(token);
assert.equal(client._transport._config.host, 'hubName.azure-devices.net');
assert.equal(client._transport._config.keyName, 'keyname');
assert.equal(client._transport._config.sharedAccessSignature, token);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_020: [The `fromSharedAccessSignature` method shall use the default Transport (Amqp) if the `Transport` optional argument is falsy.]*/
it('creates an instance of the default transport', function () {
const client = Client.fromSharedAccessSignature(token);
assert.instanceOf(client._transport, Amqp);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_019: [The `fromSharedAccessSignature` method shall use the `Transport` constructor passed as argument to instantiate a transport object if it's not falsy.]*/
it('uses the transport given as argument', function () {
const FakeTransport = function (config) {
assert.isOk(config);
};
const client = Client.fromSharedAccessSignature(token, FakeTransport);
assert.instanceOf(client._transport, FakeTransport);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_007: [The fromSharedAccessSignature method shall return a new instance of the Client object, as by a call to new Client(transport).]*/
it('returns an instance of Client', function () {
const client = Client.fromSharedAccessSignature(token);
assert.instanceOf(client, Client);
assert.isOk(client._restApiClient);
});
});
describe('#fromTokenCredential', function () {
const fakeTokenCredential = {
getToken: sinon.stub().resolves({
token: "fake_token",
expiresOnTimeStamp: Date.now() + 3600000
})
};
it('creates an instance of the default transport', function () {
const client = Client.fromTokenCredential("hub.host.tv", fakeTokenCredential);
assert.instanceOf(client._transport, Amqp);
});
it('uses the transport given as argument', function () {
const FakeTransport = function (config) {
assert.isOk(config);
};
const client = Client.fromTokenCredential("hub.host.tv", fakeTokenCredential, FakeTransport);
assert.instanceOf(client._transport, FakeTransport);
});
it('returns an instance of Client', function () {
const client = Client.fromTokenCredential("hub.host.tv", fakeTokenCredential);
assert.instanceOf(client, Client);
assert.isOk(client._restApiClient);
});
it('correctly populates the config structure', function () {
const client = Client.fromTokenCredential("hub.host.tv", fakeTokenCredential);
assert.equal(client._transport._config.host, 'hub.host.tv');
assert.equal(client._transport._config.tokenCredential, fakeTokenCredential);
assert.equal(client._restApiClient._config.host, 'hub.host.tv');
assert.equal(client._restApiClient._config.tokenCredential, fakeTokenCredential);
assert.equal(client._restApiClient._config.tokenScope, 'https://iothubs.azure.net/.default');
});
});
const goodSendParameters = [
{ obj: Buffer.from('foo'), name: 'Buffer' },
{ obj: 'foo', name: 'string' },
{ obj: [], name: 'Array' },
{ obj: new ArrayBuffer(), name: 'ArrayBuffer' }
];
const badSendParameters = [
{ obj: 1, name: 'number' },
{ obj: true, name: 'boolean' },
{ obj: {}, name: 'object' }
];
describe('#send', function () {
let testSubject;
beforeEach('prepare test subject', function () {
testSubject = new Client({}, {});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_013: [The send method shall throw ReferenceError if the deviceId or message arguments are falsy.]*/
it('throws if deviceId is falsy', function () {
assert.throws(function () {
testSubject.send(undefined, {}, () => { });
}, ReferenceError, 'deviceId is \'undefined\'');
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_013: [The send method shall throw ReferenceError if the deviceId or message arguments are falsy.]*/
it('throws if message is falsy', function () {
assert.throws(function () {
testSubject.send('id', undefined, () => { });
}, ReferenceError, 'message is \'undefined\'');
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_030: [The `send` method shall not throw if the `done` callback is falsy.]*/
it('returns a Promise done is falsy', function () {
const simulatedAmqp = new SimulatedAmqp();
const client = new Client(simulatedAmqp);
const promise = client.send('id', new Message('msg'));
assert.instanceOf(promise, Promise);
promise.catch(console.log);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_016: [The `send` method shall throw an `ArgumentError` if the `message` argument is not of type `azure-iot-common.Message` or `azure-iot-common.Message.BufferConvertible`.]*/
badSendParameters.forEach(function (testConfig) {
it('throws if message is of type ' + testConfig.name, function () {
assert.throws(function () {
testSubject.send('id', testConfig.obj, () => { });
}, errors.ArgumentError);
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_014: [The `send` method shall convert the `message` object to type `azure-iot-common.Message` if it is not already of type `azure-iot-common.Message`.]*/
goodSendParameters.forEach(function (testConfig) {
it('Converts to message if message is of type ' + testConfig.name, function (testCallback) {
const simulatedAmqp = new SimulatedAmqp();
const client = new Client(simulatedAmqp);
sinon.spy(simulatedAmqp, 'send');
client.send('id', testConfig.obj, function (err, state) {
assert(!err);
assert.equal(state.constructor.name, "MessageEnqueued");
const sentMessage = simulatedAmqp.send.firstCall.args[1];
assert.deepEqual(sentMessage, new Message(testConfig.obj));
testCallback();
});
});
});
});
describe('#basic invokeDeviceMethod', function () {
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_014: [The `invokeDeviceMethod` method shall throw a `ReferenceError` if `deviceId` is `null`, `undefined` or an empty string.]*/
[undefined, null, ''].forEach(function (badDeviceId) {
it('throws if \'deviceId\' is \'' + badDeviceId + '\'', function () {
const client = new Client({}, {});
assert.throws(function () {
client.invokeDeviceMethod(badDeviceId, 'method', { foo: 'bar' }, 42, function () {});
}, ReferenceError);
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_006: [The `invokeDeviceMethod` method shall throw a `ReferenceError` if `methodName` is `null`, `undefined` or an empty string.]*/
[undefined, null, ''].forEach(function (badMethodName) {
it('throws if \'methodParams.methodName\' is \'' + badMethodName + '\'', function () {
const client = new Client({}, {});
assert.throws(function () {
client.invokeDeviceMethod('deviceId', { methodName: badMethodName, payload: { foo: 'bar' }, timeoutInSeconds: 42 }, function () {});
}, ReferenceError);
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_007: [The `invokeDeviceMethod` method shall throw a `TypeError` if `methodName` is not a `string`.]*/
[{}, function (){}, 42].forEach(function (badMethodType) {
it('throws if \'methodParams.methodName\' is of type \'' + badMethodType + '\'', function () {
const client = new Client({}, {});
assert.throws(function () {
client.invokeDeviceMethod('deviceId', { methodName: badMethodType, payload: { foo: 'bar' }, timeoutInSeconds: 42 }, function () {});
}, TypeError);
});
});
});
describe('invokeDeviceMethod as promise', function () {
it ('Can fulfill a promise when using moduleId and methodParams', function (testCallback) {
const fakeMethodParams = {
methodName: 'method',
payload: null,
timeoutInSeconds: 42
};
const fakeResult = { foo: 'bar' };
const fakeResponse = { statusCode: 200 };
const fakeRestClient = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
callback(null, fakeResult, fakeResponse);
}
};
const client = new Client({}, fakeRestClient);
client.invokeDeviceMethod('fakeDeviceId', 'fakeModuleId', fakeMethodParams).then((promiseResult) => {
assert.strictEqual(promiseResult.result, fakeResult);
assert.strictEqual(promiseResult.message, fakeResponse);
testCallback();
})
.catch(() => {
assert.fail('promise incorrectly rejected');
});
});
it ('Can reject a promise when using using moduleId and methodParams with promise rejected', function (testCallback) {
const fakeMethodParams = {
methodName: 'method',
payload: null,
timeoutInSeconds: 42
};
const fakeError = new Error('good error');
const fakeRestClient = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
callback(fakeError);
}
};
const client = new Client({}, fakeRestClient);
client.invokeDeviceMethod('fakeDeviceId', 'fakeModuleId', fakeMethodParams).then(() => {
assert.fail('promise incorrectly fulfilled');
})
.catch((err) => {
assert.strictEqual(err, fakeError);
testCallback();
});
});
it ('Can fulfill the promise when only passing a methodParams argument', function (testCallback) {
const fakeMethodParams = {
methodName: 'method',
payload: null,
timeoutInSeconds: 42
};
const fakeResult = { foo: 'bar' };
const fakeResponse = { statusCode: 200 };
const fakeRestClient = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
callback(null, fakeResult, fakeResponse);
}
};
const client = new Client({}, fakeRestClient);
client.invokeDeviceMethod('fakeDeviceId', fakeMethodParams).then((promiseResult) => {
assert.strictEqual(promiseResult.result, fakeResult);
assert.strictEqual(promiseResult.message, fakeResponse);
testCallback();
})
.catch(() => {
assert.fail('promise incorrectly rejected');
});
});
it ('Can reject the promise when only passing a methodParams argument', function (testCallback) {
const fakeMethodParams = {
methodName: 'method',
payload: null,
timeoutInSeconds: 42
};
const fakeError = new Error('good error');
const fakeRestClient = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
callback(fakeError);
}
};
const client = new Client({}, fakeRestClient);
client.invokeDeviceMethod('fakeDeviceId', fakeMethodParams).then(() => {
assert.fail('promise incorrectly fulfilled');
})
.catch((err) => {
assert.strictEqual(err, fakeError);
testCallback();
});
});
[undefined, null, '', {}, 42].forEach(function (badMethod) {
it ('throws ReferenceError when using moduleId and methodParams is \'' + badMethod + '\'', function (testCallback) {
const client = new Client({}, {});
client.invokeDeviceMethod('fakeDeviceId', 'fakeModuleId', badMethod).then(() => {
assert.fail('promise incorrectly fulfilled');
}).catch((err) => {
assert.instanceOf(err, ReferenceError);
testCallback();
});
});
});
[{ methodName: 4 }].forEach(function (badMethodType) {
it ('throws TypeError when using moduleId and methodParams has type of \'' + badMethodType + '\'', function (testCallback) {
const client = new Client({}, {});
client.invokeDeviceMethod('fakeDeviceId', 'fakeModuleId', badMethodType).then(() => {
assert.fail('promise incorrectly fulfilled');
}).catch((err) => {
assert.instanceOf(err, TypeError);
testCallback();
});
});
});
[undefined, null, '', {}, 42].forEach(function (badMethod) {
it ('throws ReferenceError when NOT using moduleId and methodParams has type of \'' + badMethod + '\'', function (testCallback) {
const client = new Client({}, {});
client.invokeDeviceMethod('fakeDeviceId', badMethod).then(() => {
assert.fail('promise incorrectly fulfilled');
}).catch((err) => {
assert.instanceOf(err, ReferenceError);
testCallback();
});
});
});
[{ methodName: 4 }].forEach(function (badMethodType) {
it ('throws TypeError when NOT using moduleId and methodParams has type of \'' + badMethodType + '\'', function (testCallback) {
const client = new Client({}, {});
client.invokeDeviceMethod('fakeDeviceId', badMethodType).then(() => {
assert.fail('promise incorrectly fulfilled');
}).catch((err) => {
assert.instanceOf(err, TypeError);
testCallback();
});
});
});
});
[
{ functionUnderTest: function (client, param, callback) { client.invokeDeviceMethod('deviceId', param, callback); } },
{ functionUnderTest: function (client, param, callback) { client.invokeDeviceMethod('deviceId', 'moduleId', param, callback); } },
].forEach(function (testConfig) {
describe('#advanced invokeDeviceMethod', function () {
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_009: [The `invokeDeviceMethod` method shall initialize a new instance of `DeviceMethod` with the `methodName` and `timeout` values passed in the arguments.]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_010: [The `invokeDeviceMethod` method shall use the newly created instance of `DeviceMethod` to invoke the method with the `payload` argument on the device specified with the `deviceid` argument .]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_013: [The `invokeDeviceMethod` method shall call the `done` callback with a `null` first argument, the result of the method execution in the second argument, and the transport-specific response object as a third argument.]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_18_003: [If `moduleIdOrMethodParams` is a string the `invokeDeviceMethod` method shall call `invokeOnModule` on the new `DeviceMethod` instance. ]*/
it('uses the DeviceMethod client to invoke the method', function (testCallback) {
const fakeMethodParams = {
methodName: 'method',
payload: null,
timeoutInSeconds: 42
};
const fakeResult = { foo: 'bar' };
const fakeResponse = { statusCode: 200 };
const fakeRestClient = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
callback(null, fakeResult, fakeResponse);
}
};
const client = new Client({}, fakeRestClient);
testConfig.functionUnderTest(client, fakeMethodParams, function (err, result, response) {
assert.isNull(err);
assert.equal(result, fakeResult);
assert.equal(response, fakeResponse);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_012: [The `invokeDeviceMethod` method shall call the `done` callback with a standard javascript `Error` object if the request failed.]*/
it('works when payload and timeout are omitted', function (testCallback) {
const fakeError = new Error('fake error');
const fakeRestClientFails = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
callback(fakeError);
}
};
const client = new Client({}, fakeRestClientFails);
testConfig.functionUnderTest(client, { methodName: 'method' }, function (err) {
assert.equal(err, fakeError);
testCallback();
});
});
});
});
describe('#open', function () {
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_004: [The `disconnect` event shall be emitted when the client is disconnected from the server.]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_002: [If the transport successfully establishes a connection the `open` method shall subscribe to the `disconnect` event of the transport.]*/
it('subscribes to the \'disconnect\' event once connected', function (done) {
const simulatedAmqp = new SimulatedAmqp();
const client = new Client(simulatedAmqp, {});
client.open(function () {
client.on('disconnect', function () {
done();
});
simulatedAmqp.emit('disconnect');
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_009: [**When the `open` method completes, the callback function (indicated by the `done` argument) shall be invoked with the following arguments:
- `err` - standard JavaScript `Error` object (or subclass)]*/
it('calls the done callback if passed as argument', function (testCallback) {
const simulatedAmqp = new SimulatedAmqp();
const client = new Client(simulatedAmqp);
client.open(testCallback);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_006: [The `open` method should not throw if the `done` callback is not specified.]*/
it('doesn\'t throw if the done callback is not passed as argument', function () {
const simulatedAmqp = new SimulatedAmqp();
const client = new Client(simulatedAmqp);
assert.doesNotThrow(function () {
client.open();
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_011: [**Otherwise the argument `err` shall have an `amqpError` property containing implementation-specific response information for use in logging and troubleshooting.]*/
it('calls the done callback with an error if the transport fails to connect', function (testCallback) {
const fakeError = new errors.UnauthorizedError('will not retry');
const fakeTransport = new EventEmitter();
fakeTransport.connect = sinon.stub().callsArgWith(0, fakeError);
const client = new Client(fakeTransport);
client.open(function (err) {
assert.strictEqual(err, fakeError);
testCallback();
});
});
});
describe('#close', function () {
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_003: [The `close` method shall remove the listener that has been attached to the transport `disconnect` event.]*/
it('unsubscribes for the \'disconnect\' event when disconnecting', function (done) {
const simulatedAmqp = new SimulatedAmqp();
const client = new Client(simulatedAmqp, {});
let disconnectReceived = false;
client.open(function () {
client.on('disconnect', function () {
disconnectReceived = true;
});
client.close(function () {
simulatedAmqp.emit('disconnect');
assert.isFalse(disconnectReceived);
done();
});
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_022: [When the `close` method completes, the callback function (indicated by the done argument) shall be invoked with the following arguments:
- `err` - standard JavaScript `Error` object (or subclass)]*/
it('calls the done callback if passed as argument', function (testCallback) {
const simulatedAmqp = new SimulatedAmqp();
const client = new Client(simulatedAmqp);
client.close(testCallback);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_005: [The `close` method should not throw if the `done` callback is not specified.]*/
it('doesn\'t throw if the done callback is not passed as argument', function () {
const simulatedAmqp = new SimulatedAmqp();
const client = new Client(simulatedAmqp);
assert.doesNotThrow(function () {
client.close();
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_024: [Otherwise the argument `err` shall have a transport property containing implementation-specific response information for use in logging and troubleshooting.]*/
it('calls the done callback with an error if the transport fails to disconnect', function (testCallback) {
const fakeError = new errors.UnauthorizedError('will not retry');
const fakeTransport = new EventEmitter();
fakeTransport.disconnect = sinon.stub().callsArgWith(0, fakeError);
const client = new Client(fakeTransport);
client.close(function (err) {
assert.strictEqual(err, fakeError);
testCallback();
});
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_05_027: [When the `getFeedbackReceiver` method completes, the callback function (indicated by the `done` argument) shall be invoked with the following arguments:
- `err` - standard JavaScript `Error` object (or subclass): `null` if the operation was successful
- `receiver` - an `AmqpReceiver` instance: `undefined` if the operation failed.]*/
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_001: [When the `getFileNotificationReceiver` method completes, the callback function (indicated by the `done` argument) shall be invoked with the following arguments:
- `err` - standard JavaScript `Error` object (or subclass): `null` if the operation was successful
- `receiver` - an `AmqpReceiver` instance: `undefined` if the operation failed.]*/
['getFeedbackReceiver', 'getFileNotificationReceiver'].forEach(function (getReceiverMethod) {
describe(getReceiverMethod, function () {
it('calls ' + getReceiverMethod + ' on the transport', function (testCallback) {
const fakeTransport = new EventEmitter();
fakeTransport[getReceiverMethod] = sinon.stub().callsArgWith(0, null, new EventEmitter());
const client = new Client(fakeTransport);
client[getReceiverMethod](function (err, recv) {
assert.isNull(err);
assert.instanceOf(recv, EventEmitter);
testCallback();
});
});
it('calls its callback with an error if it the transport fails to provide a feedback receiver', function (testCallback) {
const fakeError = new errors.UnauthorizedError('will not retry');
const fakeTransport = new EventEmitter();
fakeTransport[getReceiverMethod] = sinon.stub().callsArgWith(0, fakeError);
const client = new Client(fakeTransport);
client[getReceiverMethod](function (err) {
assert.strictEqual(err, fakeError);
testCallback();
});
});
});
});
describe('setRetryPolicy', function () {
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_027: [The `setRetryPolicy` method shall throw a `ReferenceError` if the `policy` argument is falsy.]*/
[null, undefined].forEach(function (badPolicy) {
it('throws a ReferenceError if the policy is \'' + badPolicy + '\'', function () {
const client = new Client(new EventEmitter());
assert.throws(function () {
client.setRetryPolicy(badPolicy);
}, ReferenceError);
});
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_028: [The `setRetryPolicy` method shall throw an `ArgumentError` if the `policy` object does not have a `shouldRetry` method and a `nextRetryTimeout` method.]*/
it('throws an ArgumentError if the policy does not have a shouldRetry method', function () {
const badPolicy = { nextRetryTimeout: function () {} };
const client = new Client(new EventEmitter());
assert.throws(function () {
client.setRetryPolicy(badPolicy);
}, errors.ArgumentError);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_028: [The `setRetryPolicy` method shall throw an `ArgumentError` if the `policy` object does not have a `shouldRetry` method and a `nextRetryTimeout` method.]*/
it('throws an ArgumentError if the policy does not have a nextRetryTimeout method', function () {
const badPolicy = { shouldRetry: function () {} };
const client = new Client(new EventEmitter());
assert.throws(function () {
client.setRetryPolicy(badPolicy);
}, errors.ArgumentError);
});
/*Tests_SRS_NODE_IOTHUB_CLIENT_16_029: [Any operation (e.g. `send`, `getFeedbackReceiver`, etc) initiated after a call to `setRetryPolicy` shall use the policy passed as argument to retry.]*/
it('uses the new retry policy for all subsequent calls', function (testCallback) {
const fakeError = new errors.UnauthorizedError('will not retry');
const fakeRetryPolicy = {
shouldRetry: sinon.stub().returns(true),
nextRetryTimeout: sinon.stub().returns(1)
};
const fakeTransport = new EventEmitter();
fakeTransport.connect = sinon.stub().onFirstCall().callsArgWith(0, fakeError)
.onSecondCall().callsFake(function () {
assert.isTrue(fakeRetryPolicy.shouldRetry.calledOnce);
assert.isTrue(fakeRetryPolicy.shouldRetry.calledOnce);
testCallback();
});
const client = new Client(fakeTransport);
client.setRetryPolicy(fakeRetryPolicy);
client.open(function () {});
});
});
});
const fakeRegistry = {
create: function (device, done) { done(); },
addModule: function (module, done) { done(); },
delete: function (deviceId, done) { done(); }
};
describe('Over simulated AMQP', function () {
const opts = {
transport: function () { return new SimulatedAmqp(); },
connectionString: process.env.IOTHUB_CONNECTION_STRING,
registry: fakeRegistry
};
transportSpecificTests(opts);
});

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

@ -0,0 +1,30 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const Registry = require('../dist/registry.js').Registry;
const AmqpWs = require('../dist/amqp_ws.js').AmqpWs;
const transportSpecificTests = require('./_client_common_testrun.js');
describe('Over real AMQP (Default Transport)', function () {
// eslint-disable-next-line no-invalid-this
this.timeout(60000);
const opts = {
transport: null,
connectionString: process.env.IOTHUB_CONNECTION_STRING,
registry: Registry.fromConnectionString(process.env.IOTHUB_CONNECTION_STRING)
};
transportSpecificTests(opts);
});
describe('Over real AMQP over Websockets', function () {
// eslint-disable-next-line no-invalid-this
this.timeout(60000);
const opts = {
transport: AmqpWs,
connectionString: process.env.IOTHUB_CONNECTION_STRING,
registry: Registry.fromConnectionString(process.env.IOTHUB_CONNECTION_STRING)
};
transportSpecificTests(opts);
});

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

@ -0,0 +1,28 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const assert = require('chai').assert;
const ArgumentError = require('azure-iot-common').errors.ArgumentError;
const ConnectionString = require('../dist/connection_string.js');
const incompleteConnectionStrings = {
HostName: 'SharedAccessKeyName=keyname;SharedAccessKey=key',
SharedAccessKeyName: 'HostName=hostname;SharedAccessKey=key',
SharedAccessKey: 'HostName=name;SharedAccessKeyName=keyname'
};
describe('ConnectionString', function () {
describe('#parse', function () {
/*Tests_SRS_NODE_IOTHUB_CONNSTR_05_001: [The parse method shall return the result of calling azure-iot-common.ConnectionString.parse.]*/
/*Tests_SRS_NODE_IOTHUB_CONNSTR_05_002: [It shall throw ArgumentError if any of 'HostName', 'SharedAccessKeyName', or 'SharedAccessKey' fields are not found in the source argument.]*/
['HostName', 'SharedAccessKeyName', 'SharedAccessKey'].forEach(function (key) {
it('throws if connection string is missing ' + key, function () {
assert.throws(function () {
ConnectionString.parse(incompleteConnectionStrings[key]);
}, ArgumentError);
});
});
});
});

288
test/_device_method_test.js Normal file
Просмотреть файл

@ -0,0 +1,288 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const assert = require('chai').assert;
const versionQueryString = require('../dist/version.js').versionQueryString;
const DeviceMethod = require('../dist/device_method.js').DeviceMethod;
describe('DeviceMethod', function () {
describe('#constructor', function () {
[undefined, null, ''].forEach(function (badParams) {
it('throws a \'ReferenceError\' if params is \'' + badParams + '\'', function () {
assert.throws(function () {
return new DeviceMethod(badParams, {});
}, ReferenceError);
});
});
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_16_004: [The `DeviceMethod` constructor shall throw a `ReferenceError` if `params.methodName` is `null`, `undefined` or an empty string.]*/
[undefined, null, ''].forEach(function (badMethodName) {
it('throws a \'ReferenceError\' if params.methodName is \'' + badMethodName + '\'', function () {
assert.throws(function () {
return new DeviceMethod({ methodName: badMethodName }, {});
}, ReferenceError);
});
});
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_16_005: [The `DeviceMethod` constructor shall throw a `TypeError` if `params.methodName` is not a `string`.]*/
[{}, function () {}, 42].forEach(function (badMethodType) {
it('throws a \'TypeError\' if params.methodName is \'' + typeof badMethodType + '\'', function () {
assert.throws(function () {
return new DeviceMethod({ methodName: badMethodType }, {});
}, TypeError);
});
});
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_16_006: [The `DeviceMethod` constructor shall set the `DeviceMethod.params.timeoutInSeconds` property to the `timeoutInSeconds` argument value.]*/
it('sets the DeviceMethod.responseTimeoutInSeconds property to the responseTimeoutInSeconds argument value', function () {
const testTimeout = 42;
const method = new DeviceMethod({ methodName: 'foo', payload: null, responseTimeoutInSeconds: testTimeout }, {});
assert.equal(method.params.responseTimeoutInSeconds, testTimeout);
});
[undefined, null, 0].forEach(function (badTimeoutValue) {
it('sets the DeviceMethod.params.responseTimeoutInSeconds property to the default timeout value if params.responseTimeoutInSeconds is \'' + badTimeoutValue + '\'', function () {
const method = new DeviceMethod({ methodName: 'foo', payload: null, responseTimeoutInSeconds: badTimeoutValue }, {});
assert.equal(method.params.responseTimeoutInSeconds, DeviceMethod.defaultResponseTimeout);
});
});
/*Codes_SRS_NODE_IOTHUB_DEVICE_METHOD_16_013: [The `DeviceMethod` constructor shall set the `DeviceMethod.params.methodName` property to the `params.methodName` argument value.]*/
it('sets the DeviceMethod.name property to the methodName argument value', function () {
const methodName = 'foo';
const method = new DeviceMethod({ methodName: methodName }, {});
assert.equal(method.params.methodName, methodName);
});
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_16_015: [The `DeviceMethod` constructor shall set the `DeviceMethod.params.payload` property value to the `params.payload` argument value or to the default (`null`) if the `payload` argument is `null` or `undefined`.]*/
[-1, 0, '', {}, { foo: 'bar' }, 'foo', Buffer.from([0xDE, 0xAD, 0xBE, 0xEF])].forEach(function (goodPayload) {
it('sets the DeviceMethod.params.payload property to the params.payload argument value: \'' + goodPayload.toString() + '\'', function () {
const method = new DeviceMethod({ methodName: 'foo', payload: goodPayload, responseTimeoutInSeconds: 42 }, {});
assert.equal(method.params.payload, goodPayload);
});
});
[undefined, null].forEach(function (badPayload) {
it('sets the DeviceMethod.params.payload property to the default payload value if params.payload is \'' + badPayload + '\'', function () {
const method = new DeviceMethod({ methodName: 'foo', payload: badPayload, responseTimeoutInSeconds: 42 }, {});
assert.equal(method.params.payload, DeviceMethod.defaultPayload);
});
});
});
describe('#invokeOn', function () {
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_16_008: [The `invokeOn` method shall throw a `ReferenceError` if `deviceId` is `null`, `undefined` or an empty string.]*/
[undefined, null, ''].forEach(function (badDeviceId) {
it('throws a ReferenceError if \'deviceId\' is \'' + badDeviceId + '\'', function () {
const method = new DeviceMethod({ methodName: 'foo', payload: null, responseTimeoutInSeconds: 42 }, {});
assert.throws(function () {
method.invokeOn(badDeviceId, function () {});
}, ReferenceError);
});
});
});
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_18_001: [The `invokeOnModule` method shall throw a `ReferenceError` if `deviceId` or `moduleId` is falsy. ]*/
describe('#invokeOnModule', function () {
[undefined, null, ''].forEach(function (badArg) {
it('throws a ReferenceError if \'deviceId\' is \'' + badArg + '\'', function () {
const method = new DeviceMethod({ methodName: 'foo', payload: null, responseTimeoutInSeconds: 42 }, {});
assert.throws(function () {
method.invokeOnModule(badArg, 'moduleId', function () {});
}, ReferenceError);
});
it('throws a ReferenceError if \'moduleId\' is \'' + badArg + '\'', function () {
const method = new DeviceMethod({ methodName: 'foo', payload: null, responseTimeoutInSeconds: 42 }, {});
assert.throws(function () {
method.invokeOnModule('deviceId', badArg, function () {});
}, ReferenceError);
});
});
it('returns a promise with result argument and a transport-specific response second argument', function (done) {
const fakeResult = {
status: 'success'
};
const fakeResponse = {
statusCode: 200
};
const fakeRestClientSucceeds = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
callback(null, fakeResult, fakeResponse);
}
};
const method = new DeviceMethod({ methodName: 'foo', payload: null, responseTimeoutInSeconds: 42 }, fakeRestClientSucceeds);
method.invokeOn("deviceId").then((res) => {
assert.deepEqual(res.device, fakeResult);
assert.isDefined(res.response, fakeResponse);
done();
}).catch((err) => done(err));
});
});
const fakeDeviceId = 'deviceId';
const fakeModuleId = 'moduleId';
[
{
name: 'invokeDeviceMethod',
functionUnderTest: function (method, callback) { method.invokeOn(fakeDeviceId, callback); },
expectedPath: '/twins/' + fakeDeviceId + '/methods' + versionQueryString()
},
{
name: 'invokeDeviceMethod',
functionUnderTest: function (method, callback) { method.invokeOnModule(fakeDeviceId, fakeModuleId, callback); },
expectedPath: '/twins/' + fakeDeviceId + '/modules/' + fakeModuleId + '/methods' + versionQueryString()
},
].forEach(function (testConfig) {
describe('#' + testConfig.name, function () {
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_16_009: [The `invokeOn` method shall invoke the `done` callback with an standard javascript `Error` object if the method execution failed.]*/
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_18_003: [The `invokeOnModule` method shall invoke the `done` callback with an standard javascript `Error` object if the method execution failed. ]*/
it('calls the done callback with an Error object if the request fails', function (testCallback) {
const fakeError = new Error('Fake failure');
const fakeRestClientFails = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
callback(fakeError);
}
};
const method = new DeviceMethod({ methodName: 'foo', payload: null, responseTimeoutInSeconds: 42 }, fakeRestClientFails);
testConfig.functionUnderTest(method, function (err) {
assert.equal(err, fakeError);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_16_010: [The `invokeOn` method shall invoke the `done` callback with a `null` first argument, a result second argument and a transport-specific response third argument if the method execution succeeds**/
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_18_004: [The `invokeOnModule` method shall invoke the `done` callback with a `null` first argument, a result second argument and a transport-specific response third argument if the method execution succeeds. ]*/
it('calls the done callback with a null first argument, a result second argument and a transport-specific response third argument', function (testCallback) {
const fakeResult = {
status: 'success'
};
const fakeResponse = {
statusCode: 200
};
const fakeRestClientSucceeds = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
callback(null, fakeResult, fakeResponse);
}
};
const method = new DeviceMethod({ methodName: 'foo', payload: null, responseTimeoutInSeconds: 42 }, fakeRestClientSucceeds);
testConfig.functionUnderTest(method, function (err, result, response) {
assert.isNull(err);
assert.equal(result, fakeResult);
assert.equal(response, fakeResponse);
testCallback();
});
});
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_16_017: [The `invokeOn` method shall uri-encode the device id.]*/
it('URI-encodes the device id', function (testCallback) {
const fakeMethodParams = {
methodName: 'method',
payload: { foo: 'bar' },
responseTimeoutInSeconds: 42
};
const fakeDeviceId = 'device#';
const uriEncodedDeviceId = encodeURIComponent(fakeDeviceId);
const fakeRestClient = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
assert.equal(path, '/twins/' + uriEncodedDeviceId + '/methods' + versionQueryString());
callback();
}
};
const method = new DeviceMethod(fakeMethodParams, fakeRestClient);
method.invokeOn(fakeDeviceId, testCallback);
});
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_16_011: [The `invokeOn` method shall construct an HTTP request using information supplied by the caller, as follows:
```
POST /twins/<encodeUriComponent(deviceId>)/methods?api-version=<version> HTTP/1.1
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
{
"methodName": <DeviceMethod.params.name>,
"responseTimeoutInSeconds": <DeviceMethod.params.responseTimeoutInSeconds>,
"connectTimeoutInSeconds": <DeviceMethod.params.connectTimeoutInSeconds>,
"payload": <DeviceMethod.params.payload>
}
```]*/
/*Tests_SRS_NODE_IOTHUB_DEVICE_METHOD_18_002: [The `invokeOnModule` method shall construct an HTTP request using information supplied by the caller, as follows:
```
POST /twins/<encodeUriComponent(deviceId)>/modules/<encodeUriComponent(moduleId)>/methods?api-version=<version> HTTP/1.1
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
{
"methodName": <DeviceMethod.params.name>,
"responseTimeoutInSeconds": <DeviceMethod.params.responseTimeoutInSeconds>,
"connectTimeoutInSeconds": <DeviceMethod.params.connectTimeoutInSeconds>,
"payload": <DeviceMethod.params.payload>
}
```
]*/
[-1, 0, '', {}, { foo: 'bar' }, 'one line', Buffer.from([0xDE, 0xAD, 0xBE, 0xEF])].forEach(function (goodPayload) {
it('builds a correct request when the payload is ' + goodPayload.toString(), function (testCallback) {
const fakeMethodParams = {
methodName: 'method',
payload: { foo: 'bar' },
responseTimeoutInSeconds: 42,
connectTimeoutInSeconds: 43
};
const fakeRestClient = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
assert.equal(method, 'POST');
assert.equal(path, testConfig.expectedPath);
assert.equal(headers['Content-Type'], 'application/json; charset=utf-8');
assert.equal(body.methodName, fakeMethodParams.methodName);
assert.equal(body.responseTimeoutInSeconds, fakeMethodParams.responseTimeoutInSeconds);
assert.equal(body.connectTimeoutInSeconds, fakeMethodParams.connectTimeoutInSeconds);
assert.equal(body.payload, fakeMethodParams.payload);
assert.equal(timeout, (fakeMethodParams.responseTimeoutInSeconds + fakeMethodParams.connectTimeoutInSeconds) * 1000);
callback();
}
};
const method = new DeviceMethod(fakeMethodParams, fakeRestClient);
testConfig.functionUnderTest(method, testCallback);
});
});
[-1, 0, '', {}, { foo: 'bar' }, 'one line', Buffer.from([0xDE, 0xAD, 0xBE, 0xEF])].forEach(function (goodPayload) {
it('builds a correct request when the payload is ' + goodPayload.toString(), function (testCallback) {
const fakeMethodParams = {
methodName: 'method',
payload: goodPayload,
responseTimeoutInSeconds: 42
};
const fakeRestClient = {
executeApiCall: function (method, path, headers, body, timeout, callback) {
assert.equal(method, 'POST');
assert.equal(path, testConfig.expectedPath);
assert.equal(headers['Content-Type'], 'application/json; charset=utf-8');
assert.equal(body.methodName, fakeMethodParams.methodName);
assert.equal(body.responseTimeoutInSeconds, fakeMethodParams.responseTimeoutInSeconds);
assert.equal(body.connectTimeoutInSeconds, fakeMethodParams.connectTimeoutInSeconds);
assert.equal(body.payload, fakeMethodParams.payload);
assert.equal(timeout, (fakeMethodParams.responseTimeoutInSeconds + fakeMethodParams.connectTimeoutInSeconds) * 1000);
callback();
}
};
const method = new DeviceMethod(fakeMethodParams, fakeRestClient);
testConfig.functionUnderTest(method, testCallback);
});
});
});
});
});

63
test/_device_test.js Normal file
Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const assert = require('chai').assert;
const Device = require('../dist/device.js').Device;
describe('Device', function () {
describe('constructor', function () {
const fakeDevice = { deviceId: 'fake' };
/*Tests_SRS_NODE_SERVICE_DEVICE_16_002: [If the `deviceDescription` argument is provided as a string, it shall be parsed as JSON and the properties of the new `Device` object shall be populated with the values provided in the `deviceDescription` JSON string.]*/
it('accepts a JSON string', function () {
const testDevice = new Device(JSON.stringify(fakeDevice));
assert.strictEqual(testDevice.deviceId, fakeDevice.deviceId);
});
/*Tests_SRS_NODE_SERVICE_DEVICE_16_003: [If the `deviceDescription` argument if provided as an object, the properties of the new `Device` object shall be populated with the values provided in the `deviceDescription` JSON string.]*/
it('accepts an object', function () {
const testDevice = new Device(fakeDevice);
assert.strictEqual(testDevice.deviceId, fakeDevice.deviceId);
});
/*Tests_SRS_NODE_SERVICE_DEVICE_16_001: [The constructor shall accept a `null` or `undefined` value as argument and create an empty `Device` object.]*/
[null, undefined].forEach(function (falsyValue) {
it('does not throw if given \'' + falsyValue + '\'', function () {
assert.doesNotThrow(function () {
return new Device(falsyValue);
});
});
});
/*Tests_SRS_NODE_SERVICE_DEVICE_16_004: [The constructor shall throw a `ReferenceError` if the `deviceDescription` argument doesn't contain a `deviceId` property.]*/
it('throws if given an argument and it does not have a deviceId', function () {
const badArg = {
key: 'value'
};
assert.throws(function () {
return new Device(badArg);
}, ReferenceError);
});
});
describe('SymmetricKey', function () {
/*Tests_SRS_NODE_SERVICE_DEVICE_16_005: [The `authentication.SymmetricKey` property shall return the content of the `authentication.symmetricKey` property (the latter being the valid property returned by the IoT hub in the device description).]*/
it('returns the content of the symmetricKey property', function () {
const deviceDescription = {
deviceId: 'test',
authentication: {
symmetricKey: {
primaryKey: 'primaryKey',
secondaryKey: 'secondaryKey'
}
}
};
const testDevice = new Device(deviceDescription);
assert.deepEqual(testDevice.symmetricKey, deviceDescription.symmetricKey);
assert.deepEqual(testDevice.SymmetricKey, deviceDescription.symmetricKey);
});
});
});

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

@ -0,0 +1,310 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
const assert = require('chai').assert;
const sinon = require('sinon');
const DigitalTwinClient = require('../dist/cl/digital_twin_client').DigitalTwinClient;
const testCredentials = {
signRequest: sinon.stub().callsFake(function (webResource) {
return Promise.resolve(webResource);
}),
getHubName: sinon.stub().returns('fake.host.name')
};
describe('DigitalTwinClient', function () {
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_001: [The `DigitalTwinClient` creates an instance of the DigitalTwinClient passing IoTHubTokenCredentials class as an argument.]*/
it(`Constructor creates an instance of the DigitalTwinClient`, function (testCallback) {
const digitalTwinClient = new DigitalTwinClient(testCredentials);
assert.instanceOf(digitalTwinClient, DigitalTwinClient);
testCallback();
});
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_002: [The `getDigitalTwin` method shall call the `getDigitalTwin` method of the protocol layer with the given argument.]*/
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_003: [The `getDigitalTwin` method shall call the callback with an error parameter if a callback is passed..]*/
it('getDigitalTwin calls the getDigitalTwin method on the PL client', function (testCallback) {
const testTwinId = 'digitalTwinId';
const testDigitalTwin = {
interfaces: {
testInterfaceInstanceName: {}
},
response: undefined
};
const testClient = new DigitalTwinClient(testCredentials);
testClient._pl.digitalTwin.getDigitalTwin = sinon.stub().callsArgWith(1, null, testDigitalTwin);
testClient.getDigitalTwin(testTwinId, function (err, result, _response) {
assert.isTrue(testClient._pl.digitalTwin.getDigitalTwin.calledWith(testTwinId));
assert.isNull(err);
assert.deepEqual(result.interfaces, testDigitalTwin.interfaces);
assert.deepEqual(result.response, testDigitalTwin.response);
testCallback();
});
});
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_004: [The `getDigitalTwin` method shall return error if the method of the protocol layer failed.]*/
it('getDigitalTwin calls its callback with an error if the PL client fails', function (testCallback) {
const testTwinId = 'digitalTwinId';
const testError = new Error('fake error');
const testClient = new DigitalTwinClient(testCredentials);
testClient._pl.digitalTwin.getDigitalTwin = sinon.stub().callsArgWith(1, testError);
testClient.getDigitalTwin(testTwinId, function (err) {
assert.isTrue(testClient._pl.digitalTwin.getDigitalTwin.calledWith(testTwinId));
assert.strictEqual(err, testError);
testCallback();
});
});
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_020: [The `getDigitalTwin` method shall return a promise if there is no callback passed.]*/
it('getDigitalTwin shall return a promise if there is no callback passed', async function () {
const testTwinId = 'digitalTwinId';
const testDigitalTwin = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testClient = new DigitalTwinClient(testCredentials);
testClient._pl.digitalTwin.getDigitalTwin = sinon.stub().callsArgWith(1, null, testDigitalTwin);
const returnedPromise = await testClient.getDigitalTwin(testTwinId);
assert.deepEqual(returnedPromise.interfaces, testDigitalTwin.interfaces);
assert.deepEqual(returnedPromise.response, testDigitalTwin.response);
});
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_011: [The `updateDigitalTwin` method shall call the `updateDigitalTwin` method of the protocol layer with the given arguments.]*/
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_012: [The `updateDigitalTwin` method shall call the callback with an error parameter if a callback is passed..]*/
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_028: [** The `patch` argument of the `updateDigitalTwin` method should be a JSON string using the following format:
const patch = {
interfaces: {
[interfaceInstanceName]: {
properties: {
[propertyName]: {
desired: {
value: propertyValue
}
}
}
}
}
};
The interfaceInstanceName should be an existing interfaceInstance's name.
The propertyName could be existing or new.
The patch should contain difference to a previously reported twin only (e.g. patch).]*/
it('updateDigitalTwin calls updateDigitalTwin on the PL client', function (testCallback) {
const testTwinId = 'digitalTwinId';
const testPropertyValue ='testPropertyValue';
const testServicePatch = {
interfaces: {
interfaceInstanceName: {
properties: {
testPropertyName: {
value: testPropertyValue
}
}
}
},
version: 1234
};
const testUserPatch = {
interfaces: {
interfaceInstanceName: {
properties: {
testPropertyName: {
value: testPropertyValue
}
}
}
},
version: 1234
};
const testClient = new DigitalTwinClient(testCredentials);
testClient._pl.digitalTwin.updateDigitalTwin = sinon.stub().callsArgWith(3, null, testServicePatch, null, undefined);
testClient.updateDigitalTwin(testTwinId, testUserPatch, function (err, result) {
assert.isTrue(testClient._pl.digitalTwin.updateDigitalTwin.calledWith(testTwinId, testUserPatch));
assert.isNull(err);
assert.deepEqual(result, testUserPatch);
testCallback();
});
});
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_013: [The `updateDigitalTwin` method shall return error if the method of the protocol layer failed.]*/
it('updateDigitalTwin calls its callback with an error if the PL client fails', function (testCallback) {
const testTwinId = 'digitalTwinId';
const testPatch = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testError = new Error('fake error');
const testClient = new DigitalTwinClient(testCredentials);
testClient._pl.digitalTwin.updateDigitalTwin = sinon.stub().callsArgWith(3, testError);
testClient.updateDigitalTwin(testTwinId, testPatch, function (err, _result) {
assert.isTrue(testClient._pl.digitalTwin.updateDigitalTwin.calledWith(testTwinId));
assert.strictEqual(err, testError);
testCallback();
});
});
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_023: [The `updateDigitalTwin` method shall return a promise if there is no callback passed.]*/
it('updateDigitalTwin shall return a promise if there is no callback passed', async function () {
const testTwinId = 'digitalTwinId';
const testDigitalTwin = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testPatch = {
eTag: '001'
};
const testClient = new DigitalTwinClient(testCredentials);
testClient._pl.digitalTwin.updateDigitalTwin = sinon.stub().callsArgWith(3, null, testDigitalTwin, null, undefined);
const returnedPromise = await testClient.updateDigitalTwin(testTwinId, testPatch);
assert.deepEqual(returnedPromise, testDigitalTwin);
});
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_026: [The `updateDigitalTwin` method shall call the `updateDigitalTwin` method of the protocol layer with the given arguments including eTag.]*/
it('updateDigitalTwin calls updateDigitalTwin on the PL client using eTag', function (testCallback) {
const testTwinId = 'digitalTwinId';
const eTag = 'testETag';
const testPropertyValue ='testPropertyValue';
const testServicePatch = {
interfaces: {
testInterfaceInstanceName: {
properties: {
testPropertyName: {
value: testPropertyValue
}
}
}
},
version: undefined
};
const testResponse = {
parsedHeaders: {
eTag: '001'
}
};
const testUserPatch = {
interfaces: {
testInterfaceInstanceName: {
properties: {
testPropertyName: {
value: testPropertyValue
}
}
}
},
version: undefined
};
const testClient = new DigitalTwinClient(testCredentials);
testClient._pl.digitalTwin.updateDigitalTwin = sinon.stub().callsArgWith(3, null, testServicePatch, null, testResponse);
testClient.updateDigitalTwin(testTwinId, testUserPatch, eTag, function (err, result) {
assert.isTrue(testClient._pl.digitalTwin.updateDigitalTwin.calledWith(testTwinId, testUserPatch));
assert.isNull(err);
assert.deepEqual(result, testUserPatch);
testCallback();
});
});
/* Tests_SRS_NODE_DIGITAL_TWIN_CLIENT_12_030: [The `updateDigitalTwin` method shall return a promise if eTag is passed and there is no callback passed.] */
it('updateDigitalTwin shall return a promise if eTag is passed and there is no callback passed', async function () {
const testTwinId = 'digitalTwinId';
const eTag = 'eTag';
const testServicePatch = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testResponse = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testUserPatch = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testClient = new DigitalTwinClient(testCredentials);
testClient._pl.digitalTwin.updateDigitalTwin = sinon.stub().callsArgWith(3, null, testServicePatch, null, testResponse);
const returnedPromise = await testClient.updateDigitalTwin(testTwinId, testUserPatch, eTag);
assert.deepEqual(returnedPromise._response, testUserPatch);
});
it('invokeComponentCommand called with the right parameters and shall return a promise', async function () {
const testTwinId = 'digitalTwinId';
const testCommandResponse = {
result: {
testResult: {}
}
};
const testInterfaceInstanceName = 'testInterfaceInstanceName';
const testCommandName = 'testCommandName';
const testArgument = 123456;
const testClient = new DigitalTwinClient(testCredentials);
sinon.stub(testClient._pl.digitalTwin, "invokeComponentCommand").returns(testCommandResponse);
const response = await testClient.invokeComponentCommand(testTwinId, testInterfaceInstanceName, testCommandName, testArgument);
assert.isTrue(testClient._pl.digitalTwin.invokeComponentCommand.calledWith(testTwinId, testInterfaceInstanceName, testCommandName, testArgument));
assert.isNotNull(response);
assert.strictEqual(testCommandResponse, response);
});
it('invokeComponentCommand called with the right plus optional parameters and shall return a promise', async function () {
const testTwinId = 'digitalTwinId';
const testCommandResponse = {
result: {
testResult: {}
}
};
const testInterfaceInstanceName = 'testInterfaceInstanceName';
const testCommandName = 'testCommandName';
const testArgument = 123456;
const options = {};
const testClient = new DigitalTwinClient(testCredentials);
sinon.stub(testClient._pl.digitalTwin, "invokeComponentCommand").returns(testCommandResponse);
const response = await testClient.invokeComponentCommand(testTwinId, testInterfaceInstanceName, testCommandName, testArgument, options);
assert.isTrue(testClient._pl.digitalTwin.invokeComponentCommand.calledWith(testTwinId, testInterfaceInstanceName, testCommandName, testArgument));
assert.isNotNull(response);
assert.strictEqual(testCommandResponse, response);
});
it('invokeCommand called with the right plus optional parameters and shall return a promise', async function () {
const testTwinId = 'digitalTwinId';
const testCommandResponse = {
result: {
testResult: {}
}
};
const testCommandName = 'testCommandName';
const testArgument = 123456;
const testClient = new DigitalTwinClient(testCredentials);
sinon.stub(testClient._pl.digitalTwin, "invokeRootLevelCommand").returns(testCommandResponse);
const response = await testClient.invokeCommand(testTwinId, testCommandName, testArgument);
assert.isTrue(testClient._pl.digitalTwin.invokeRootLevelCommand.calledWith(testTwinId, testCommandName, testArgument));
assert.isNotNull(response);
assert.strictEqual(testCommandResponse, response);
});
// eslint-disable-next-line mocha/no-identical-title
it('invokeCommand called with the right plus optional parameters and shall return a promise', async function () {
const testTwinId = 'digitalTwinId';
const testCommandResponse = {
result: {
testResult: {}
}
};
const testCommandName = 'testCommandName';
const testArgument = 123456;
const options = {};
const testClient = new DigitalTwinClient(testCredentials);
sinon.stub(testClient._pl.digitalTwin, "invokeRootLevelCommand").returns(testCommandResponse);
const response = await testClient.invokeCommand(testTwinId, testCommandName, testArgument, options);
assert.isTrue(testClient._pl.digitalTwin.invokeRootLevelCommand.calledWith(testTwinId, testCommandName, testArgument));
assert.isNotNull(response);
assert.strictEqual(testCommandResponse, response);
});
});

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

@ -0,0 +1,563 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
const assert = require('chai').assert;
const sinon = require('sinon');
const IoTHubRegistryManager = require('../dist/cl/iothub_registry_manager').IoTHubRegistryManager;
const testCredentials = {
signRequest: sinon.stub().callsFake(function (webResource) {
return Promise.resolve(webResource);
}),
getHubName: sinon.stub().returns('fake.host.name')
};
describe('IoTHubRegistryManager', function () {
it(`Constructor creates an instance of the IoTHubRegistryManager`, function (testCallback) {
const ioTHubRegistryManager = new IoTHubRegistryManager(testCredentials);
assert.instanceOf(ioTHubRegistryManager, IoTHubRegistryManager);
testCallback();
});
it('createDeviceWithSas calls the createOrUpdateIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testPrimaryKey = 'testPrimaryKey';
const testSecondaryKey = 'testSecondaryKey';
const testIsEnabled = true;
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.createOrUpdateIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.createDeviceWithSas(testDeviceId, testPrimaryKey, testSecondaryKey, testIsEnabled);
assert.isTrue(testClient._pl.devices.createOrUpdateIdentity.calledWith(
testDeviceId,
sinon.match.has('deviceId', testDeviceId)
.and(sinon.match.has('status', 'enabled'))
.and(sinon.match.has('authentication'))
.and(sinon.match.hasNested('authentication.type', 'sas'))
.and(sinon.match.hasNested('authentication.symmetricKey'))
.and(sinon.match.hasNested('authentication.symmetricKey.primaryKey', testPrimaryKey))
.and(sinon.match.hasNested('authentication.symmetricKey.secondaryKey', testSecondaryKey))
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('createDeviceWithX509 calls the createOrUpdateIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testPrimaryThumbprint = 'primaryThumbprint';
const testSecondaryThumbprint = 'secondaryThumbprint';
const testIsEnabled = true;
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.createOrUpdateIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.createDeviceWithX509(testDeviceId, testPrimaryThumbprint, testSecondaryThumbprint, testIsEnabled);
assert.isTrue(testClient._pl.devices.createOrUpdateIdentity.calledWith(
testDeviceId,
sinon.match.has('deviceId', testDeviceId)
.and(sinon.match.has('status', 'enabled'))
.and(sinon.match.has('authentication'))
.and(sinon.match.hasNested('authentication.type', 'selfSigned'))
.and(sinon.match.hasNested('authentication.x509Thumbprint'))
.and(sinon.match.hasNested('authentication.x509Thumbprint.primaryThumbprint', testPrimaryThumbprint))
.and(sinon.match.hasNested('authentication.x509Thumbprint.secondaryThumbprint', testSecondaryThumbprint))
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('createDeviceWithCertificateAuthority calls the createOrUpdateIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testIsEnabled = true;
const testIoTEdge = true;
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.createOrUpdateIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.createDeviceWithCertificateAuthority(testDeviceId, testIsEnabled, testIoTEdge);
assert.isTrue(testClient._pl.devices.createOrUpdateIdentity.calledWith(
testDeviceId,
sinon.match.has('deviceId', testDeviceId)
.and(sinon.match.has('status', 'enabled'))
.and(sinon.match.has('capabilities'))
.and(sinon.match.hasNested('capabilities.iotEdge', testIoTEdge))
.and(sinon.match.has('authentication'))
.and(sinon.match.hasNested('authentication.type', 'certificateAuthority'))
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('updateDevice calls the createOrUpdateIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testDevice = 'testDevice';
const testIfMatch = {
ifMatch: '*'
}
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.createOrUpdateIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.updateDevice(testDeviceId, testDevice);
assert.isTrue(testClient._pl.devices.createOrUpdateIdentity.calledWith(
testDeviceId,
testDevice,
testIfMatch
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('getDevice calls the getIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.getIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.getDevice(testDeviceId);
assert.isTrue(testClient._pl.devices.getIdentity.calledWith(
testDeviceId
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('deleteDevice calls the deleteIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testETag = 'testETag';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.deleteIdentity = sinon.stub().resolves(returnValue);
await testClient.deleteDevice(testDeviceId, testETag);
assert.isTrue(testClient._pl.devices.deleteIdentity.calledWith(
testDeviceId,
sinon.match.has('ifMatch', testETag)
));
});
it('createModuleWithSas calls the createOrUpdateIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testManagedBy = 'testManagedBy';
const testPrimaryKey = 'testPrimaryKey';
const testSecondaryKey = 'testSecondaryKey';
const testIsEnabled = true;
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.createOrUpdateIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.createModuleWithSas(testDeviceId, testModuleId, testManagedBy, testPrimaryKey, testSecondaryKey, testIsEnabled);
assert.isTrue(testClient._pl.modules.createOrUpdateIdentity.calledWith(
testDeviceId,
testModuleId,
sinon.match.has('deviceId', testDeviceId)
.and(sinon.match.has('moduleId', testModuleId))
.and(sinon.match.has('managedBy', testManagedBy))
.and(sinon.match.has('authentication'))
.and(sinon.match.hasNested('authentication.type', 'sas'))
.and(sinon.match.hasNested('authentication.symmetricKey'))
.and(sinon.match.hasNested('authentication.symmetricKey.primaryKey', testPrimaryKey))
.and(sinon.match.hasNested('authentication.symmetricKey.secondaryKey', testSecondaryKey))
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('createModuleWithX509 calls the createOrUpdateIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testManagedBy = 'testManagedBy';
const testPrimaryThumbprint = 'primaryThumbprint';
const testSecondaryThumbprint = 'secondaryThumbprint';
const testIsEnabled = true;
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.createOrUpdateIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.createModuleWithX509(testDeviceId, testModuleId, testManagedBy, testPrimaryThumbprint, testSecondaryThumbprint, testIsEnabled);
assert.isTrue(testClient._pl.modules.createOrUpdateIdentity.calledWith(
testDeviceId,
testModuleId,
sinon.match.has('deviceId', testDeviceId)
.and(sinon.match.has('moduleId', testModuleId))
.and(sinon.match.has('managedBy', testManagedBy))
.and(sinon.match.has('authentication'))
.and(sinon.match.hasNested('authentication.type', 'selfSigned'))
.and(sinon.match.hasNested('authentication.x509Thumbprint'))
.and(sinon.match.hasNested('authentication.x509Thumbprint.primaryThumbprint', testPrimaryThumbprint))
.and(sinon.match.hasNested('authentication.x509Thumbprint.secondaryThumbprint', testSecondaryThumbprint))
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('createModuleWithCertificateAuthority calls the createOrUpdateIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testManagedBy = 'testManagedBy';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.createOrUpdateIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.createModuleWithCertificateAuthority(testDeviceId, testModuleId, testManagedBy);
assert.isTrue(testClient._pl.modules.createOrUpdateIdentity.calledWith(
testDeviceId,
testModuleId,
sinon.match.has('deviceId', testDeviceId)
.and(sinon.match.has('moduleId', testModuleId))
.and(sinon.match.has('managedBy', testManagedBy))
.and(sinon.match.has('authentication'))
.and(sinon.match.hasNested('authentication.type', 'certificateAuthority'))
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('updateModule calls the createOrUpdateIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testModule = 'testModule';
const testIfMatch = {
ifMatch: '*'
}
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.createOrUpdateIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.updateModule(testDeviceId, testModuleId, testModule);
assert.isTrue(testClient._pl.modules.createOrUpdateIdentity.calledWith(
testDeviceId,
testModuleId,
testModule,
testIfMatch
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('getModule calls the getIdentity method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.getIdentity = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.getModule(testDeviceId, testModuleId);
assert.isTrue(testClient._pl.modules.getIdentity.calledWith(
testDeviceId,
testModuleId
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('getModules calls the getModulesOnDevice method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.getModulesOnDevice = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.getModules(testDeviceId);
assert.isTrue(testClient._pl.modules.getModulesOnDevice.calledWith(
testDeviceId
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('deleteModule calls the deleteIdentity method on the PL client', async function () {
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testETag = 'testETag';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.deleteIdentity = sinon.stub().resolves();
await testClient.deleteModule(testDeviceId, testModuleId, testETag);
assert.isTrue(testClient._pl.modules.deleteIdentity.calledWith(
testDeviceId,
testModuleId,
sinon.match.has('ifMatch', testETag)
));
});
it('getServiceStatistics calls the getServiceStatistics method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.statistics.getServiceStatistics = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.getServiceStatistics();
assert.isTrue(testClient._pl.statistics.getServiceStatistics.calledWith());
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('getDeviceRegistryStatistics calls the getDeviceStatistics method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.statistics.getDeviceStatistics = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.getDeviceRegistryStatistics();
assert.isTrue(testClient._pl.statistics.getDeviceStatistics.calledWith());
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('getDevices calls the getDevices method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testMaxNumberOfDevices = 42;
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.getDevices = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.getDevices(testMaxNumberOfDevices);
assert.isTrue(testClient._pl.devices.getDevices.calledWith(
sinon.match.has('top', testMaxNumberOfDevices)
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('bulkCreateOrUpdateDevices calls the updateRegistry method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDevices = ['testDevice1', 'testDevice2'];
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.bulkRegistry.updateRegistry = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.bulkCreateOrUpdateDevices(testDevices);
assert.isTrue(testClient._pl.bulkRegistry.updateRegistry.calledWith(
testDevices
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('queryIoTHub calls the getTwins method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testQuerySpecification = 'testQuerySpecification';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.query.getTwins = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.queryIoTHub(testQuerySpecification);
assert.isTrue(testClient._pl.query.getTwins.calledWith(
testQuerySpecification
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('getTwin calls the getTwin method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.getTwin = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.getTwin(testDeviceId);
assert.isTrue(testClient._pl.devices.getTwin.calledWith(
testDeviceId
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('replaceTwin calls the replaceTwin method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testDeviceTwin = 'testDeviceTwin';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.replaceTwin = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.replaceTwin(testDeviceId, testDeviceTwin);
assert.isTrue(testClient._pl.devices.replaceTwin.calledWith(
testDeviceId,
testDeviceTwin
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('updateTwin calls the updateTwin method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testDeviceTwin = 'testDeviceTwin';
const testETag = 'testETag';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.updateTwin = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.updateTwin(testDeviceId, testDeviceTwin, testETag);
assert.isTrue(testClient._pl.devices.updateTwin.calledWith(
testDeviceId,
testDeviceTwin,
sinon.match.has('ifMatch', testETag)
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('getModuleTwin calls the getTwin method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.getTwin = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.getModuleTwin(testDeviceId, testModuleId);
assert.isTrue(testClient._pl.modules.getTwin.calledWith(
testDeviceId,
testModuleId
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('replaceModuleTwin calls the replaceTwin method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testDeviceTwin = 'testDeviceTwin';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.replaceTwin = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.replaceModuleTwin(testDeviceId, testModuleId, testDeviceTwin);
assert.isTrue(testClient._pl.modules.replaceTwin.calledWith(
testDeviceId,
testModuleId,
testDeviceTwin
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('updateModuleTwin calls the updateTwin method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testModuleTwin = 'testModuleTwin';
const testETag = 'testETag';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.updateTwin = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.updateModuleTwin(testDeviceId, testModuleId, testModuleTwin, testETag);
assert.isTrue(testClient._pl.modules.updateTwin.calledWith(
testDeviceId,
testModuleId,
testModuleTwin,
sinon.match.has('ifMatch', testETag)
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('invokeDeviceMethod calls the invokeMethod method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testDirectMethodRequest = 'testDirectMethodRequest';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.devices.invokeMethod = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.invokeDeviceMethod(testDeviceId, testDirectMethodRequest);
assert.isTrue(testClient._pl.devices.invokeMethod.calledWith(
testDeviceId,
testDirectMethodRequest
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
it('invokeDeviceModuleMethod calls the invokeMethod method on the PL client', async function () {
const returnValue = {
interfaces: {
testInterfaceInstanceName: {}
}
};
const testDeviceId = 'testDeviceId';
const testModuleId = 'testModuleId';
const testDirectMethodRequest = 'testDirectMethodRequest';
const testClient = new IoTHubRegistryManager(testCredentials);
testClient._pl.modules.invokeMethod = sinon.stub().resolves(returnValue);
const returnedPromise = await testClient.invokeDeviceModuleMethod(testDeviceId, testModuleId, testDirectMethodRequest);
assert.isTrue(testClient._pl.modules.invokeMethod.calledWith(
testDeviceId,
testModuleId,
testDirectMethodRequest
));
assert.deepEqual(returnedPromise.interfaces, returnValue.interfaces);
});
});

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

@ -0,0 +1,37 @@
const assert = require('chai').assert;
const sinon = require('sinon');
const IoTHubTokenCredentials = require('../dist/auth/iothub_token_credentials').IoTHubTokenCredentials;
describe('IoTHubTokenCredentials', function () {
describe('getHubName', function () {
it('returns the Azure IoT hub hostname contained in the connection string', function () {
const testHostName = 'host.name';
const testConnectionString = 'HostName=' + testHostName + ';SharedAccessKeyName=service;SharedAccessKey=' + Buffer.from('testkey').toString('base64');
const testCreds = new IoTHubTokenCredentials(testConnectionString);
assert.strictEqual(testCreds.getHubName(), testHostName);
});
});
describe('signRequest', function () {
it('adds a shared access signature in the authorization header', function (testCallback) {
const testConnectionString = 'HostName=host.name;SharedAccessKeyName=service;SharedAccessKey=' + Buffer.from('testkey').toString('base64');
const testCreds = new IoTHubTokenCredentials(testConnectionString);
const testRequest = {
headers: {
set: sinon.stub().callsFake(function (headerName, headerContent) {
assert.strictEqual(headerName, 'authorization');
assert.include(headerContent, 'SharedAccessSignature');
assert.include(headerContent, 'sr=');
assert.include(headerContent, 'se=');
assert.include(headerContent, 'sig=');
})
}
};
testCreds.signRequest(testRequest).then(function (updatedRequest) {
assert.strictEqual(updatedRequest, testRequest); // verify it's the same object
testCallback();
});
});
});
});

641
test/_job_client_test.js Normal file
Просмотреть файл

@ -0,0 +1,641 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
"use strict";
const assert = require('chai').assert;
const sinon = require('sinon');
const uuid = require('uuid');
const versionQueryString = require('../dist/version').versionQueryString;
const JobClient = require('../dist/job_client').JobClient;
const ConnectionString = require('../iothub').ConnectionString;
const SharedAccessSignature = require('../iothub').SharedAccessSignature;
const DeviceMethod = require('../dist/device_method').DeviceMethod;
const RestApiClient = require('azure-iot-http-base').RestApiClient;
const Query = require('../dist/query').Query;
const defaultMaxExecutionTimeInSeconds = 3600;
describe('JobClient', function () {
function testFalsyArg(fn, badArgName, badArgValue, args) {
it('throws a ReferenceError when ' + badArgName + ' is \'' + badArgValue + '\'', function () {
assert.throws(function () {
return fn.apply(null, args);
}, ReferenceError);
});
}
function testBadTypeArg(fn, badArgName, badArgValue, args) {
it('throws a TypeError when ' + badArgName + ' is of type \'' + typeof(badArgValue) + '\'', function () {
assert.throws(function () {
return fn.apply(null, args);
}, TypeError);
});
}
function testCallback(fnName, args) {
/*Tests_SRS_NODE_JOB_CLIENT_16_027: [The method shall call the `done` callback with a single argument that is a standard Javascript `Error` object if the request failed.]*/
it('calls the done callback with a single Error parameter if the request fails (' + (args.length + 1) + ' arguments)', function (cb) {
const fakeError = new Error('fake');
const fakeRestApiClient = { executeApiCall: sinon.stub().callsArgWith(4, fakeError) };
const failArgs = args.slice();
failArgs.push(function (err) {
assert.strictEqual(err, fakeError);
cb();
});
const client = new JobClient(fakeRestApiClient);
client[fnName].apply(client, failArgs);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_028: [The method shall call the `done` callback with a `null` error argument, a result and a transport-specific response object if the request was successful.]*/
it('calls the done callback with a null error, a result and a response if the request succeeds (' + (args.length + 1) + ' arguments)', function (cb) {
const fakeResponse = { statusCode: 200 };
const fakeResult = { success: true };
const fakeRestApiClient = { executeApiCall: sinon.stub().callsArgWith(4, null, fakeResult, fakeResponse) };
const successArgs = args.slice();
successArgs.push(function (err, result, response) {
assert.isNull(err);
assert.strictEqual(result, fakeResult);
assert.strictEqual(response, fakeResponse);
cb();
});
const client = new JobClient(fakeRestApiClient);
client[fnName].apply(client, successArgs);
});
}
describe('#constructor', function () {
/*Tests_SRS_NODE_JOB_CLIENT_16_001: [The `JobClient` constructor shall throw a `ReferenceError` if `restApiClient` is falsy.]*/
[undefined, null].forEach(function (badClient) {
it('throws a ReferenceError if restApiClient is \'' + badClient + '\'', function () {
assert.throws(function () {
return new JobClient(badClient);
}, ReferenceError);
});
});
it('Will update the authentication expiration for each new request', function (done) {
const clock = sinon.useFakeTimers();
try {
clock.tick(1); // Expiration in the SAS we are about to create shouldn't be zero.
//
// Building up arguments for the invoke method job client call upcoming.
// Using this api because that is where the problem was reported for the Job Client
//
const fakeJobId = 'id';
const fakeQuery = 'SELECT * FROM devices';
const fakeMethodParams = {
methodName: 'name',
payload: { foo: 'bar' },
responseTimeoutInSeconds: 15
};
const fakeStartTime = new Date(Date.now() + 3600);
const fakeMaxExecutionTime = 7200;
//
// Build up the arguments for the RestApi constructor.
// Using the uuid as the key since putting a hard constant that looks
// like a key would get flagged by checks looking for keys committed to
// the repo.
//
const connectionString = 'HostName=fake.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=' + uuid.v4();
const cn = ConnectionString.parse(connectionString);
const config = {
host: cn.HostName,
sharedAccessSignature: SharedAccessSignature.create(cn.HostName, cn.SharedAccessKeyName, cn.SharedAccessKey, Date.now())
};
assert(Number(config.sharedAccessSignature.se) > 0, 'Initial sas expiration needs to be greater than 0');
//
// We create our own http base client since we want to capture
// the authorization header after it's been instantiated.
// The other functions are used during the course of serialization
// of the http request.
//
const fakeHttpBase = {
buildRequest: sinon.stub().returns({
write: sinon.stub(),
end: sinon.stub()
}),
setOptions: sinon.stub()
};
let client = new JobClient(new RestApiClient(config, 'a/1.0.0', fakeHttpBase));
client.scheduleDeviceMethod(fakeJobId, fakeQuery, fakeMethodParams, fakeStartTime, fakeMaxExecutionTime, function fakeCallback() {});
//
// The expiration should be at least 5 minutes from the epoch above. (It's very likely 1 hour).
//
// The first index of the args is the particular invocation of the buildRequest. In this case 0 is the first call.
// The second index of the args is the particular argument of the call. In this case 2 is the
// third argument to buildRequest, which are the headers of the http request.
//
const firstExpiration = Number(SharedAccessSignature.parse(fakeHttpBase.buildRequest.args[0][2].Authorization).se);
assert(firstExpiration >= ((60*5) + 1), 'Expiration of sas for first method invocation call needed to be at least 5 minutes');
//
// Move time forward by 10 seconds.
// The expiration of the second call should be at least 10 seconds later than the first call.
//
// eslint-disable-next-line no-invalid-this
clock.tick(10*1000);
client.scheduleDeviceMethod(fakeJobId, fakeQuery, fakeMethodParams, fakeStartTime, fakeMaxExecutionTime, function fakeCallback() {});
const secondExpiration = Number(SharedAccessSignature.parse(fakeHttpBase.buildRequest.args[1][2].Authorization).se);
assert(secondExpiration >= (firstExpiration + 10), 'Expiration of sas for second method invocation needed to be at least 10 seconds after first');
} finally {
clock.restore();
}
done();
});
});
describe('#fromConnectionString', function () {
/*Tests_SRS_NODE_JOB_CLIENT_16_002: [The `fromConnectionString` method shall throw a `ReferenceError` if `connectionString` is falsy.]*/
[undefined, null, ''].forEach(function (badValue) {
testFalsyArg(JobClient.fromConnectionString, 'connectionString', badValue, [badValue]);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_003: [The `fromConnectionString` method shall return a new `JobClient` instance.]*/
it('returns a JobClient instance', function () {
const fakeConnectionString = 'HostName=bad;SharedAccessKeyName=keyName;SharedAccessKey=key';
assert.instanceOf(JobClient.fromConnectionString(fakeConnectionString), JobClient);
});
});
describe('#fromSharedAccessSignature', function () {
/*Tests_SRS_NODE_JOB_CLIENT_16_004: [The `fromSharedAccessSignature` method shall throw a `ReferenceError` if `sharedAccessSignature` is falsy.]*/
[undefined, null, ''].forEach(function (badValue) {
testFalsyArg(JobClient.fromSharedAccessSignature, 'sharedAccessSignature', badValue, [badValue]);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_005: [The `fromSharedAccessSignature` method shall return a new `JobClient` instance.]*/
it('returns a JobClient instance', function () {
const fakeSas = 'SharedAccessSignature sr=bad&sig=XLU2ibNOYBbld3FpFIOHbPZv3Thp4wfK%2BcqZpJz66hE%3D&skn=keyName&se=1474440492';
assert.instanceOf(JobClient.fromSharedAccessSignature(fakeSas), JobClient);
});
});
describe('#fromTokenCredential', function () {
const fakeTokenCredential = {
getToken: sinon.stub().resolves({
token: "fake_token",
expiresOnTimeStamp: Date.now() + 3600000
})
};
it('returns a JobClient instance', function () {
assert.instanceOf(JobClient.fromTokenCredential("hub.host.tv", fakeTokenCredential), JobClient);
});
it('correctly populates the config structure', function () {
const client = JobClient.fromTokenCredential("hub.host.tv", fakeTokenCredential);
assert.equal(client._restApiClient._config.host, 'hub.host.tv');
assert.equal(client._restApiClient._config.tokenCredential, fakeTokenCredential);
assert.equal(client._restApiClient._config.tokenScope, 'https://iothubs.azure.net/.default');
});
});
describe('#getJob', function () {
/*Tests_SRS_NODE_JOB_CLIENT_16_006: [The `getJob` method shall throw a `ReferenceError` if `jobId` is `null`, `undefined` or an empty string.]*/
[undefined, null, ''].forEach(function (badValue) {
testFalsyArg(new JobClient({}).getJob, 'jobId', badValue, [badValue, () => {}]);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_007: [The `getJob` method shall construct the HTTP request as follows:
```
GET /jobs/v2/<jobId>?api-version=<version>
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
```]*/
it('creates a valid HTTP request', function () {
const fakeJobId = 'foo';
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
client.getJob(fakeJobId, function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'GET');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/' + fakeJobId + versionQueryString());
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][2], null);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3], null);
});
testCallback('getJob', ['jobId']);
});
describe('#createQuery', function () {
it('returns a Query object', function () {
const client = new JobClient({});
assert.instanceOf(client.createQuery(), Query);
});
});
describe('#_getJobsFunc', function () {
/*Tests_SRS_NODE_JOB_CLIENT_16_012: [The `createQuery` method shall construct the HTTP request as follows:
```
GET /jobs/v2/query?api-version=<version>&jobType=<jobType>&jobStatus=<jobStatus>
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
```]*/
it('creates a valid HTTP request when no arguments are given', function () {
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
const query = client.createQuery();
query.next(function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'GET');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/query' + versionQueryString());
assert.deepEqual(fakeRestApiClient.executeApiCall.args[0][2], {});
assert.isNull(fakeRestApiClient.executeApiCall.args[0][3]);
});
it('creates a valid HTTP request with jobType only', function () {
const fakeJobType = 'type';
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
const query = client.createQuery(fakeJobType);
query.next(function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'GET');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/query' + versionQueryString() + '&jobType=' + fakeJobType);
assert.deepEqual(fakeRestApiClient.executeApiCall.args[0][2], {});
assert.isNull(fakeRestApiClient.executeApiCall.args[0][3]);
});
it('creates a valid HTTP request with jobStatus only', function () {
const fakeJobStatus = 'status';
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
const query = client.createQuery(null, fakeJobStatus);
query.next(function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'GET');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/query' + versionQueryString() + '&jobStatus=' + fakeJobStatus);
assert.deepEqual(fakeRestApiClient.executeApiCall.args[0][2], {});
assert.isNull(fakeRestApiClient.executeApiCall.args[0][3]);
});
it('creates a valid HTTP request with pageSize only', function () {
const fakePageSize = 42;
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
const query = client.createQuery(null, null, fakePageSize);
query.next(function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'GET');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/query' + versionQueryString());
assert.deepEqual(fakeRestApiClient.executeApiCall.args[0][2], { 'x-ms-max-item-count': fakePageSize });
assert.isNull(fakeRestApiClient.executeApiCall.args[0][3]);
});
it('creates a valid HTTP request with all parameters', function () {
const fakeJobType = 'type';
const fakeJobStatus = 'status';
const fakePageSize = 42;
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
const query = client.createQuery(fakeJobType, fakeJobStatus, fakePageSize);
query.next(function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'GET');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/query' + versionQueryString() + '&jobStatus=' + fakeJobStatus + '&jobType=' + fakeJobType);
assert.deepEqual(fakeRestApiClient.executeApiCall.args[0][2], { 'x-ms-max-item-count': fakePageSize });
assert.isNull(fakeRestApiClient.executeApiCall.args[0][3]);
});
it('creates a valid HTTP request with a continuationToken', function (testCallback) {
const fakeToken = 'testToken';
const fakeRestApiClient = { executeApiCall: sinon.stub().callsArgWith(4, null, [], { statusCode: 200, headers: { 'x-ms-continuation': fakeToken } }) };
const client = new JobClient(fakeRestApiClient);
const query = client.createQuery();
query.next(function () {
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'GET');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/query' + versionQueryString());
assert.deepEqual(fakeRestApiClient.executeApiCall.args[0][2], {});
assert.isNull(fakeRestApiClient.executeApiCall.args[0][3]);
query.next(function () {
assert.strictEqual(fakeRestApiClient.executeApiCall.args[1][0], 'GET');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[1][1], '/jobs/v2/query' + versionQueryString());
assert.deepEqual(fakeRestApiClient.executeApiCall.args[1][2], { 'x-ms-continuation': fakeToken });
assert.isNull(fakeRestApiClient.executeApiCall.args[1][3]);
testCallback();
});
});
});
});
describe('#cancelJob', function () {
/*Tests_SRS_NODE_JOB_CLIENT_16_008: [The `cancelJob` method shall throw a `ReferenceError` if `jobId` is `null`, `undefined` or an empty string.]*/
[undefined, null, ''].forEach(function (badValue) {
testFalsyArg(new JobClient({}).cancelJob, 'jobId', badValue, [badValue, () => {}]);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_009: [The `cancelJob` method shall construct the HTTP request as follows:
```
POST /jobs/v2/<jobId>/cancel?api-version=<version>
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
```]*/
it('creates a valid HTTP request', function () {
const fakeJobId = 'foo';
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
client.cancelJob(fakeJobId, function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'POST');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/' + fakeJobId + '/cancel' + versionQueryString());
assert.isNull(fakeRestApiClient.executeApiCall.args[0][2]);
assert.isNull(fakeRestApiClient.executeApiCall.args[0][3]);
});
testCallback('cancelJob', ['jobId']);
});
describe('#scheduleDeviceMethod', function () {
const goodParams = {
methodName: 'name',
responseTimeoutInSeconds: 15,
payload: null
};
/*Tests_SRS_NODE_JOB_CLIENT_16_013: [The `scheduleDeviceMethod` method shall throw a `ReferenceError` if `jobId` is `null`, `undefined` or an empty string.]*/
[undefined, null, ''].forEach(function (badJobId) {
testFalsyArg(new JobClient({}).scheduleDeviceMethod, 'jobId', badJobId, [badJobId, 'SELECT * FROM devices', goodParams, () => {}]);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_014: [The `scheduleDeviceMethod` method shall throw a `ReferenceError` if `queryCondition` is falsy.]*/
[undefined, null, ''].forEach(function (badQuery) {
testFalsyArg(new JobClient({}).scheduleDeviceMethod, 'queryCondition', badQuery, ['id', badQuery, goodParams, () => {}]);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_029: [The `scheduleDeviceMethod` method shall throw a `ReferenceError` if `methodParams` is falsy.]*/
[undefined, null].forEach(function (badParams) {
testFalsyArg(new JobClient({}).scheduleDeviceMethod, 'methodParams', badParams, ['id', 'SELECT * FROM devices', badParams, () => {}]);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_015: [The `scheduleDeviceMethod` method shall throw a `ReferenceError` if `methodParams.methodName` is `null`, `undefined` or an empty string.]*/
[undefined, null, ''].forEach(function (badMethodName) {
testFalsyArg(new JobClient({}).scheduleDeviceMethod, 'methodParams.methodName', badMethodName, ['id', 'SELECT * FROM devices', { methodName: badMethodName }, () => {}]);
});
[42, {}, function () {}].forEach(function (badQueryType) {
testBadTypeArg(new JobClient({}).scheduleDeviceMethod, 'queryCondition', badQueryType, ['id', badQueryType, { methodName: 'name' }, new Date(), 3600, () => {}]);
});
it('throws a TypeError if the callback is not the last parameter', function () {
const client = new JobClient({});
assert.throws(function () {
client.scheduleDeviceMethod('id', 'query', { methodName: 'name' }, function () {}, 3600);
}, TypeError);
assert.throws(function () {
client.scheduleDeviceMethod('id', 'query', { methodName: 'name' }, new Date(), function () {}, 3600);
}, TypeError);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_030: [The `scheduleDeviceMethod` method shall use the `DeviceMethod.defaultPayload` value if `methodParams.payload` is `undefined`.]*/
/*Tests_SRS_NODE_JOB_CLIENT_16_031: [The `scheduleDeviceMethod` method shall use the `DeviceMethod.defaultResponseTimeout` value if `methodParams.responseTimeoutInSeconds` is falsy.]*/
it('uses the default payload and responseTimeoutInSeconds if either of those methodParams properties are undefined', function () {
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
client.scheduleDeviceMethod('id', 'SELECT * FROM devices', { methodName: 'name' }, new Date(), 86400, function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].cloudToDeviceMethod.payload, DeviceMethod.defaultPayload);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].cloudToDeviceMethod.responseTimeoutInSeconds, DeviceMethod.defaultResponseTimeout);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_020: [The `scheduleDeviceMethod` method shall construct the HTTP request as follows:
```
PUT /jobs/v2/<jobId>?api-version=<version>
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
{
jobId: '<jobId>',
type: 'scheduleDirectRequest',
cloudToDeviceMethod: <methodParams>,
queryCondition: '<queryCondition>', // if the query parameter is a string
startTime: <jobStartTime>, // as an ISO-8601 date string
maxExecutionTimeInSeconds: <maxExecutionTimeInSeconds> // Number of seconds
}
```]*/
it('creates a valid HTTP request, given a proper SQL string', function () {
const fakeJobId = 'id';
const fakeQuery = 'SELECT * FROM devices';
const fakeMethodParams = {
methodName: 'name',
payload: { foo: 'bar' },
responseTimeoutInSeconds: 15
};
const fakeStartTime = new Date(Date.now() + 3600);
const fakeMaxExecutionTime = 7200;
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
client.scheduleDeviceMethod(fakeJobId, fakeQuery, fakeMethodParams, fakeStartTime, fakeMaxExecutionTime, function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'PUT');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/' + fakeJobId + versionQueryString());
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][2]['Content-Type'], 'application/json; charset=utf-8');
assert.deepEqual(fakeRestApiClient.executeApiCall.args[0][3].cloudToDeviceMethod, fakeMethodParams);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].type, 'scheduleDeviceMethod');
assert.isUndefined(fakeRestApiClient.executeApiCall.args[0][3].deviceIds);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].queryCondition, fakeQuery);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].startTime, fakeStartTime.toISOString());
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].maxExecutionTimeInSeconds, fakeMaxExecutionTime);
});
it('sends default start time and execution time if parameters not specified', function () {
const clock = sinon.useFakeTimers();
try {
const fakeTimeNowString = (new Date()).toISOString();
const fakeJobId = 'id';
const fakeQuery = 'SELECT * FROM devices';
const fakeMethodParams = {
methodName: 'name',
payload: { foo: 'bar' },
responseTimeoutInSeconds: 15
};
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
client.scheduleDeviceMethod(fakeJobId, fakeQuery, fakeMethodParams, function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].startTime, fakeTimeNowString);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].maxExecutionTimeInSeconds, defaultMaxExecutionTimeInSeconds);
} finally {
clock.restore();
}
});
it('sends default max execution time if parameter not specified', function () {
const fakeJobId = 'id';
const fakeQuery = 'SELECT * FROM devices';
const fakeMethodParams = {
methodName: 'name',
payload: { foo: 'bar' },
responseTimeoutInSeconds: 15
};
const fakeStartTime = new Date(Date.now() + 3600);
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
client.scheduleDeviceMethod(fakeJobId, fakeQuery, fakeMethodParams, fakeStartTime, function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].maxExecutionTimeInSeconds, defaultMaxExecutionTimeInSeconds);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_018: [If `jobStartTime` is a function, `jobStartTime` shall be considered the callback and a `TypeError` shall be thrown if `maxExecutionTimeInSeconds` and/or `done` are not `undefined`.]*/
testCallback('scheduleDeviceMethod', ['jobId', 'query', { methodName: 'name' }]);
/*Tests_SRS_NODE_JOB_CLIENT_16_019: [If `maxExecutionTimeInSeconds` is a function, `maxExecutionTimeInSeconds` shall be considered the callback and a `TypeError` shall be thrown if `done` is not `undefined`.]*/
testCallback('scheduleDeviceMethod', ['jobId', 'query', { methodName: 'name' }, new Date()]);
testCallback('scheduleDeviceMethod', ['jobId', 'query', { methodName: 'name' }, new Date(), 3600]);
});
describe('scheduleTwinUpdate', function () {
const goodPatch = {
tags: {
key: 'value'
}
};
/*Tests_SRS_NODE_JOB_CLIENT_16_021: [The `scheduleTwinUpdate` method shall throw a `ReferenceError` if `jobId` is `null`, `undefined` or an empty string.]*/
[undefined, null, ''].forEach(function (badValue) {
testFalsyArg(new JobClient({}).scheduleTwinUpdate, 'jobId', badValue, [badValue, 'SELECT * FROM devices', goodPatch, () => {}]);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_022: [The `scheduleTwinUpdate` method shall throw a `ReferenceError` if `queryCondition` is falsy.]*/
[undefined, null, ''].forEach(function (badValue) {
testFalsyArg(new JobClient({}).scheduleTwinUpdate, 'queryCondition', badValue, ['id', badValue, goodPatch, () => {}]);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_023: [The `scheduleTwinUpdate` method shall throw a `ReferenceError` if `patch` is falsy.]*/
[undefined, null, ''].forEach(function (badValue) {
testFalsyArg(new JobClient({}).scheduleTwinUpdate, 'patch', badValue, ['id', 'SELECT * FROM devices', badValue, () => {}]);
});
[42, {}, function () {}].forEach(function (badQueryType) {
testBadTypeArg(new JobClient({}).scheduleTwinUpdate, 'queryCondition', badQueryType, ['id', badQueryType, { tags: null }, new Date(), 3600, () => {}]);
});
it('throws a TypeError if the callback is not the last parameter', function () {
const client = new JobClient({});
assert.throws(function () {
client.scheduleTwinUpdate('id', 'query', { tags: null }, function () {}, 3600);
}, TypeError);
assert.throws(function () {
client.scheduleTwinUpdate('id', 'query', { tags: null }, new Date(), function () {}, 3600);
}, TypeError);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_026: [The `scheduleTwinUpdate` method shall construct the HTTP request as follows:
```
PUT /jobs/v2/<jobId>?api-version=<version>
Authorization: <config.sharedAccessSignature>
Content-Type: application/json; charset=utf-8
Request-Id: <guid>
User-Agent: <sdk-name>/<sdk-version>
{
jobId: '<jobId>',
type: 'scheduleTwinUpdate',
updateTwin: <patch> // Valid JSON object
queryCondition: '<queryCondition>', // if the queryCondition parameter is a string
startTime: <jobStartTime>, // as an ISO-8601 date string
maxExecutionTimeInSeconds: <maxExecutionTimeInSeconds> // Number of seconds
}
```]*/
it('creates a valid HTTP request, given a proper SQL string', function () {
const fakeJobId = 'id';
const fakeQuery = 'SELECT * FROM devices';
const fakePatch = {
tags: {
key: 'value'
}
};
const fakeStartTime = new Date(Date.now() + 3600);
const fakeMaxExecutionTime = 7200;
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
client.scheduleTwinUpdate(fakeJobId, fakeQuery, fakePatch, fakeStartTime, fakeMaxExecutionTime, function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][0], 'PUT');
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][1], '/jobs/v2/' + fakeJobId + versionQueryString());
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][2]['Content-Type'], 'application/json; charset=utf-8');
assert.deepEqual(fakeRestApiClient.executeApiCall.args[0][3].updateTwin, fakePatch);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].type, 'scheduleUpdateTwin');
assert.isUndefined(fakeRestApiClient.executeApiCall.args[0][3].deviceIds);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].queryCondition, fakeQuery);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].startTime, new Date(fakeStartTime).toISOString());
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].maxExecutionTimeInSeconds, fakeMaxExecutionTime);
});
it('sends default start time and execution time if parameters not specified', function () {
const clock = sinon.useFakeTimers();
try {
const fakeTimeNowString = (new Date()).toISOString();
const fakeJobId = 'id';
const fakeQuery = 'SELECT * FROM devices';
const fakePatch = {
tags: {
key: 'value'
}
};
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
client.scheduleTwinUpdate(fakeJobId, fakeQuery, fakePatch, function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].startTime, fakeTimeNowString);
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].maxExecutionTimeInSeconds, defaultMaxExecutionTimeInSeconds);
} finally {
clock.restore();
}
});
it('sends default max execution time if parameter not specified', function () {
const fakeJobId = 'id';
const fakeQuery = 'SELECT * FROM devices';
const fakePatch = {
tags: {
key: 'value'
}
};
const fakeStartTime = new Date(Date.now() + 3600);
const fakeRestApiClient = { executeApiCall: sinon.stub() };
const client = new JobClient(fakeRestApiClient);
client.scheduleTwinUpdate(fakeJobId, fakeQuery, fakePatch, fakeStartTime, function () {});
assert.strictEqual(fakeRestApiClient.executeApiCall.args[0][3].maxExecutionTimeInSeconds, defaultMaxExecutionTimeInSeconds);
});
/*Tests_SRS_NODE_JOB_CLIENT_16_024: [If `jobStartTime` is a function, `jobStartTime` shall be considered the callback and a `TypeError` shall be thrown if `maxExecutionTimeInSeconds` and/or `done` are not `undefined`.]*/
testCallback('scheduleTwinUpdate', ['jobId', 'query', { tags: null }]);
/*Tests_SRS_NODE_JOB_CLIENT_16_025: [If `maxExecutionTimeInSeconds` is a function, `maxExecutionTimeInSeconds` shall be considered the callback and a `TypeError` shall be thrown if `done` is not `undefined`.]*/
testCallback('scheduleTwinUpdate', ['jobId', 'query', { tags: null }, new Date()]);
testCallback('scheduleTwinUpdate', ['jobId', 'query', { tags: null }, new Date(), 3600]);
});
});

253
test/_query_test.js Normal file
Просмотреть файл

@ -0,0 +1,253 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const assert = require('chai').assert;
const sinon = require('sinon');
const Query = require('../dist/query.js').Query;
const Twin = require('../dist/twin.js').Twin;
describe('Query', function () {
describe('#constructor', function () {
/*Tests_SRS_NODE_SERVICE_QUERY_16_001: [The `Query` shall throw a `ReferenceError` if `executeQueryFn` is falsy.]*/
[undefined, null, ''].forEach(function (badFn) {
it('throws a ReferenceError if executeQueryFn is \'' + badFn + '\'', function () {
assert.throws(function (){
return new Query(badFn);
}, ReferenceError);
});
});
/*Tests_SRS_NODE_SERVICE_QUERY_16_011: [The `Query` constructor shall throw a `TypeError` if `executeQueryFn` is not a function.]*/
['foo', 42, {}].forEach(function (badFn) {
it('throws a TypeError if executeQueryFn is \'' + badFn + '\'', function () {
assert.throws(function (){
return new Query(badFn);
}, TypeError);
});
});
/*Tests_SRS_NODE_SERVICE_QUERY_16_015: [The `Query` constructor shall initialize the `hasMoreResults` property to `true.]*/
it('initializes Query.hasMoreResults to true', function () {
const query = new Query(function () {});
assert.isTrue(query.hasMoreResults);
});
});
const nextCommonTests = function (nextName) {
/*Tests_SRS_NODE_SERVICE_QUERY_16_006: [The `next` method shall set the `Query.continuationToken` property to the `continuationToken` value of the query result.]*/
it('sets the continuationToken property when a new page of results is received', function (testCallback) {
const fakeContinuationToken = 'continuationToken';
const fakeResponse = {
statusCode: 200,
headers: {
'x-ms-continuation': fakeContinuationToken
}
};
const fakeExecuteFn = sinon.stub().callsArgWith(1, null, [], fakeResponse);
const query = new Query(fakeExecuteFn);
query[nextName](function () {
assert.strictEqual(query.continuationToken, fakeContinuationToken);
testCallback();
});
});
/*Tests_SRS_NODE_SERVICE_QUERY_16_017: [the `next` method shall use the `continuationToken` passed as argument instead of its own property `Query.continuationToken` if it's not falsy.]*/
it('uses the continuationToken given as argument', function (testCallback) {
const fakeContinuationToken = 'firstContinuationToken';
const fakeResponse = {
statusCode: 200,
headers: {
'x-ms-continuation': 'secondContinuationToken'
}
};
const fakeExecuteFn = sinon.stub().callsArgWith(1, null, [], fakeResponse);
const query = new Query(fakeExecuteFn);
query[nextName](fakeContinuationToken, function () {
assert(fakeExecuteFn.calledWith(fakeContinuationToken));
testCallback();
});
});
/*Tests_SRS_NODE_SERVICE_QUERY_16_016: [** If `continuationToken` is a function and `done` is undefined the `next` method shall assume that `continuationToken` is actually the callback and us it as such (see requirements associated with the `done` parameter)]*/
it('uses the previous continuationToken in the following query', function (testCallback) {
const fakeContinuationToken = 'continuationToken';
const fakeResponse = {
statusCode: 200,
headers: {
'x-ms-continuation': fakeContinuationToken
}
};
const fakeExecuteFn = sinon.stub().callsArgWith(1, null, [], fakeResponse);
const query = new Query(fakeExecuteFn);
query[nextName](function () {
assert(fakeExecuteFn.calledWith(null));
query[nextName](function () {
assert(fakeExecuteFn.calledWith(fakeContinuationToken));
testCallback();
});
});
});
/*Tests_SRS_NODE_SERVICE_QUERY_16_013: [The `next` method shall set the `Query.hasMoreResults` property to `true` if the `continuationToken` property of the result object is not `null`.]*/
/*Tests_SRS_NODE_SERVICE_QUERY_16_014: [The `next` method shall set the `Query.hasMoreResults` property to `false` if the `continuationToken` property of the result object is `null`.]*/
[{ token: 'a string', more: true }, { token: undefined, more: false }].forEach(function (testConfig) {
it('sets hasMoreResults to \'' + testConfig.more + '\' if the continuationToken is \'' + testConfig.token + '\'', function (testCallback) {
const fakeExecuteFn = sinon.stub().callsArgWith(1, null, [], { statusCode: 200, headers: { 'x-ms-continuation': testConfig.token } });
const query = new Query(fakeExecuteFn);
query[nextName](function () {
assert.equal(query.hasMoreResults, testConfig.more);
testCallback();
});
});
});
/*Tests_SRS_NODE_SERVICE_QUERY_16_008: [The `next` method shall call the `done` callback with a single argument that is an instance of the standard Javascript `Error` object if the request failed.]*/
it('calls the done callback with an error if the request fails', function (testCallback) {
const fakeError = new Error('fake');
const fakeExecuteFn = sinon.stub().callsArgWith(1, fakeError);
const query = new Query(fakeExecuteFn);
query[nextName](function (err) {
assert.equal(err, fakeError);
testCallback();
});
});
};
describe('#next', function () {
nextCommonTests('next');
/*Tests_SRS_NODE_SERVICE_QUERY_16_007: [The `next` method shall call the `done` callback with a `null` error object, the results of the query and the response of the underlying transport if the request was successful.]*/
it('calls the done callback with the results', function (testCallback) {
const fakeResults = [
{ deviceId: 'deviceId1' },
{ deviceId: 'deviceId2' },
{ deviceId: 'deviceId3' }
];
const fakeResponse = {
statusCode: 200,
headers: {
'x-ms-continuation': null
}
};
const fakeExecuteFn = sinon.stub().callsArgWith(1, null, fakeResults, fakeResponse);
const query = new Query(fakeExecuteFn);
query.next(function (err, result, response) {
assert.isNull(err);
assert.equal(result, fakeResults);
assert.equal(response, fakeResponse);
testCallback();
});
});
it('returns a Promise when no callback is passed', function (testCallback) {
const fakeContinuationToken = 'continuationToken';
const fakeResponse = {
statusCode: 200,
headers: {
'x-ms-continuation': fakeContinuationToken
}
};
const fakeExecuteFn = sinon.stub().callsArgWith(1, null, [], fakeResponse);
const query = new Query(fakeExecuteFn);
const promise = query.next("continuationToken");
assert.typeOf(promise, "Promise");
promise.then((res) => {
assert.deepEqual(res.message, fakeResponse);
assert.isDefined(res.result);
testCallback();
}).catch((err) => testCallback(err));
});
});
describe('#nextAsTwin', function () {
nextCommonTests('nextAsTwin');
/*Tests_SRS_NODE_SERVICE_QUERY_16_009: [The `nextAsTwin` method shall call the `done` callback with a `null` error object and a collection of `Twin` objects created from the results of the query if the request was successful.]*/
it('calls the done callback with the results converted to Twin', function (testCallback) {
const fakeResults = [];
for(let i = 0; i < 3; i++) {
fakeResults.push({ deviceId: 'deviceId' + i });
}
const fakeResponse = {
statusCode: 200,
headers: {
'x-ms-continuation': null
}
};
const fakeExecuteFn = sinon.stub().callsArgWith(1, null, fakeResults, fakeResponse);
const query = new Query(fakeExecuteFn, {});
query.nextAsTwin(function (err, result, response) {
assert.isNull(err);
assert.equal(response, fakeResponse);
result.forEach(function (twin, index) {
assert.instanceOf(twin, Twin);
assert.equal(twin.deviceId, 'deviceId' + index);
});
testCallback();
});
});
it('calls the done callback with null if the result of the query was null', function (testCallback) {
const fakeResults = [];
for(let i = 0; i < 3; i++) {
fakeResults.push({ deviceId: 'deviceId' + i });
}
const fakeResponse = {
statusCode: 200,
headers: {
'x-ms-continuation': null
}
};
const fakeExecuteFn = sinon.stub().callsArgWith(1, null, null, fakeResponse);
const query = new Query(fakeExecuteFn, {});
query.nextAsTwin(function (err, result, response) {
assert.isNull(err);
assert.equal(response, fakeResponse);
assert.isNull(result);
testCallback();
});
});
it('returns a Promise when no callback is passed', function (testCallback) {
const fakeContinuationToken = 'continuationToken';
const fakeResponse = {
statusCode: 200,
headers: {
'x-ms-continuation': fakeContinuationToken
}
};
const fakeExecuteFn = sinon.stub().callsArgWith(1, null, [], fakeResponse);
const query = new Query(fakeExecuteFn);
const promise = query.nextAsTwin("continuationToken");
assert.typeOf(promise, "Promise");
promise.then((res) => {
assert.deepEqual(res.message, fakeResponse);
assert.isDefined(res.result);
testCallback();
}).catch((err) => testCallback(err));
});
});
});

2058
test/_registry_test.js Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,170 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const assert = require('chai').assert;
const uuid = require('uuid');
const Registry = require('../dist/registry.js').Registry;
const ConnectionString = require('../dist/connection_string.js');
function makeConnectionString(host, policy, key) {
return 'HostName=' + host + ';SharedAccessKeyName=' + policy + ';SharedAccessKey=' + key;
}
const goodConnectionString = process.env.IOTHUB_CONNECTION_STRING;
const cn = ConnectionString.parse(goodConnectionString);
const badConnectionStrings = [
makeConnectionString('bad' + uuid.v4() + '.net', cn.SharedAccessKeyName, cn.SharedAccessKey),
makeConnectionString(cn.HostName, 'bad', cn.SharedAccessKey),
makeConnectionString(cn.HostName, cn.SharedAccessKeyName, 'bad'),
];
let basicDevice = {
deviceId: 'testDevice' + Math.random(),
status: 'disabled'
};
const x509Device = {
deviceId: 'testDevice' + Math.random(),
starts: 'disabled',
authentication: {
x509Thumbprint : {
primaryThumbprint: '0000000000000000000000000000000000000000',
secondaryThumbprint: '1111111111111111111111111111111111111111'
}
}
};
function badConfigTests(opName, badConnStrings, requestFn) {
function makeRequestWith(connString, test, done) {
const registry = Registry.fromConnectionString(connString);
requestFn(registry, function (err, dev, res) {
test(err, dev, res);
done();
});
}
function expectNotFoundError(err) {
assert.include(err.message, 'getaddrinfo ENOTFOUND');
}
function expect401Response(err) {
assert.isNotNull(err);
assert.equal(err.response.statusCode, 401);
}
const tests = [
{ name: 'hostname is malformed', expect: expectNotFoundError },
{ name: 'policy does not exist', expect: expect401Response },
{ name: 'key is wrong', expect: expect401Response }
];
badConnStrings.forEach(function (connStr, index) {
it('fails to ' + opName + ' when the ' + tests[index].name, function (done) {
makeRequestWith(connStr, tests[index].expect, done);
});
});
}
describe('Over real HTTPS', function () {
// eslint-disable-next-line no-invalid-this
this.timeout(60000);
describe('Registry', function () {
describe('#create', function () {
[basicDevice, x509Device].forEach(function (device) {
it('creates a new device with deviceId: ' + device.deviceId, function (done) {
const registry = Registry.fromConnectionString(goodConnectionString);
registry.create(device, function (err, dev, response) {
if (err) {
done(err);
} else {
assert.equal(device.deviceId, dev.deviceId);
assert.notEqual(device, dev);
assert.equal(response.statusCode, 200);
done();
}
});
});
});
badConfigTests('create device information', badConnectionStrings, function (registry, done) {
registry.create(basicDevice, done);
});
});
describe('#get', function () {
it('returns information about the given device', function (done) {
const registry = Registry.fromConnectionString(goodConnectionString);
registry.get(basicDevice.deviceId, function (err, dev, response) {
if (err) {
done(err);
} else {
assert.equal(basicDevice.deviceId, dev.deviceId);
assert.equal(response.statusCode, 200);
basicDevice = dev;
done();
}
});
});
badConfigTests('get device information', badConnectionStrings, function (registry, done) {
registry.get(basicDevice.deviceId, done);
});
});
describe('#list', function () {
it('returns information about a list of devices', function (done) {
const registry = Registry.fromConnectionString(goodConnectionString);
registry.list(function (err, deviceList, response) {
if (err) {
done(err);
} else {
assert.isArray(deviceList);
assert.equal(response.statusCode, 200);
done();
}
});
});
badConfigTests('list device information', badConnectionStrings, function (registry, done) {
registry.list(done);
});
});
describe('#update', function () {
it('updates information about a device', function (done) {
const registry = Registry.fromConnectionString(goodConnectionString);
registry.update(basicDevice, function (err, dev, response) {
if (err) {
done(err);
} else {
assert.equal(basicDevice.deviceId, dev.deviceId);
assert.equal(response.statusCode, 200);
done();
}
});
});
badConfigTests('update device information', badConnectionStrings, function (registry, done) {
registry.update(basicDevice, done);
});
});
describe('#delete', function () {
[basicDevice, x509Device].forEach(function (device) {
it('deletes the given device with deviceId: ' + device.deviceId, function (done) {
const registry = Registry.fromConnectionString(goodConnectionString);
registry.delete(device.deviceId, function (err) {
done(err);
});
});
});
badConfigTests('delete device information', badConnectionStrings, function (registry, done) {
registry.delete(basicDevice.deviceId, done);
});
});
});
});

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

@ -0,0 +1,98 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const EventEmitter = require('events').EventEmitter;
const assert = require('chai').assert;
const sinon = require('sinon');
const Message = require('azure-iot-common').Message;
const ServiceReceiver = require('../dist/service_receiver.js').ServiceReceiver;
describe('ServiceReceiver', function () {
describe('constructor', function () {
/*Tests_SRS_NODE_SERVICE_RECEIVER_16_001: [The constructor shall subscribe to the `message` event of the `ReceiverLink` object passed as argument.]*/
/*Tests_SRS_NODE_SERVICE_RECEIVER_16_002: [The constructor shall subscribe to the `error` event of the `ReceiverLink` object passed as argument.]*/
it('subscribes to the \'message\' and \'error\' events of the ReceiverLink passed as argument', function () {
const fakeLink = new EventEmitter();
sinon.spy(fakeLink, 'on');
let _receiver = new ServiceReceiver(fakeLink);
assert.isTrue(fakeLink.on.calledTwice);
assert.isTrue(fakeLink.on.calledWith('message'));
assert.isTrue(fakeLink.on.calledWith('error'));
});
/*Tests_SRS_NODE_SERVICE_RECEIVER_16_006: [The `ServiceReceiver` class shall convert any `AmqpMessage` received with the `message` event from the `ReceiverLink` object into `Message` objects and emit a `message` event with that newly created `Message` object for argument.]*/
it('translates then forwards the message events', function (testCallback) {
const fakeLink = new EventEmitter();
sinon.spy(fakeLink, 'on');
const receiver = new ServiceReceiver(fakeLink);
const fakeAmqpMessage = {
body: 'foo'
};
receiver.on('message', function (msg) {
assert.instanceOf(msg, Message);
assert.strictEqual(msg.transportObj, fakeAmqpMessage);
testCallback();
});
fakeLink.emit('message', fakeAmqpMessage);
});
/*Tests_SRS_NODE_SERVICE_RECEIVER_16_007: [Any error event received from the `ReceiverLink` object shall be forwarded as is.]*/
it('forwards the error events', function (testCallback) {
const fakeLink = new EventEmitter();
sinon.spy(fakeLink, 'on');
const receiver = new ServiceReceiver(fakeLink);
const fakeError = new Error('fake');
receiver.on('error', function (err) {
assert.strictEqual(err, fakeError);
testCallback();
});
fakeLink.emit('error', fakeError);
});
});
/*Tests_SRS_NODE_SERVICE_RECEIVER_16_003: [The `complete` method shall call the `complete` method on the `ReceiverLink` object and pass it the `AmqpMessage` stored within the `transportObj` property of the `Message` object as well as the `done` callback passed as argument.]*/
/*Tests_SRS_NODE_SERVICE_RECEIVER_16_004: [The `abandon` method shall call the `abandon` method on the `ReceiverLink` object and pass it the `AmqpMessage` stored within the `transportObj` property of the `Message` object as well as the `done` callback passed as argument.]*/
/*Tests_SRS_NODE_SERVICE_RECEIVER_16_005: [The `reject` method shall call the `reject` method on the `ReceiverLink` object and pass it the `AmqpMessage` stored within the `transportObj` property of the `Message` object as well as the `done` callback passed as argument.]*/
['abandon', 'complete', 'reject'].forEach(function (methodName) {
describe('#' + methodName, function () {
it('calls the \'' + methodName + '\' method on the link object and passes it the AmqpMessage and the callback', function () {
const fakeLink = new EventEmitter();
fakeLink[methodName] = sinon.stub().callsArg(1);
const receiver = new ServiceReceiver(fakeLink);
const fakeMessage = new Message();
fakeMessage.transportObj = {};
const fakeCallback = function () {};
receiver[methodName](fakeMessage, fakeCallback);
assert.isTrue(fakeLink[methodName].calledWith(fakeMessage.transportObj, fakeCallback));
});
});
});
describe('forceDetach', function () {
/*Tests_SRS_NODE_SERVICE_RECEIVER_16_009: [The `forceDetach` method shall call the `forceDetach` method on the `ReceiverLink` object and pass it its `err` argument.]*/
it('calls forceDetach on the ReceiverLink with the error', function () {
const fakeLink = new EventEmitter();
fakeLink.forceDetach = sinon.stub();
const receiver = new ServiceReceiver(fakeLink);
const fakeError = new Error('fake');
receiver.forceDetach(fakeError);
assert.isTrue(fakeLink.forceDetach.calledWith(fakeError));
});
});
describe('detach', function () {
/*Tests_SRS_NODE_SERVICE_RECEIVER_16_008: [The `detach` method shall call the `detach` method on the `ReceiverLink` object and pass it its `callback` argument.]*/
it('calls detach on the ReceiverLink with the callback', function () {
const fakeLink = new EventEmitter();
fakeLink.detach = sinon.stub();
const receiver = new ServiceReceiver(fakeLink);
const fakeCallback = function () {};
receiver.detach(fakeCallback);
assert.isTrue(fakeLink.detach.calledWith(fakeCallback));
});
});
});

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

@ -0,0 +1,42 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
const assert = require('chai').assert;
const ArgumentError = require('azure-iot-common').errors.ArgumentError;
const SharedAccessSignature = require('../dist/shared_access_signature.js');
const incompleteSignatures = {
sr: 'SharedAccessSignature sig=signature&skn=keyname&se=expiry',
sig: 'SharedAccessSignature sr=audience&skn=keyname&se=expiry',
skn: 'SharedAccessSignature sr=audience&sig=signature&se=expiry',
se: 'SharedAccessSignature sr=audience&sig=signature&skn=keyname'
};
describe('SharedAccessSignature', function () {
describe('#create', function () {
/*Tests_SRS_NODE_IOTHUB_SAS_05_003: [The create method shall return the result of calling azure-iot-common.SharedAccessSignature.create with following arguments:
resourceUri - host
keyName - policy
key - key
expiry - expiry]*/
it('creates a shared access signature', function () {
const expect = 'SharedAccessSignature sr=host&sig=88JmrIsVYOGmRSjCO1x1LLbv0K001Gikh1rjfJqbQXA%3D&skn=policy&se=12345';
const sas = SharedAccessSignature.create('host', 'policy', 'key', 12345);
assert.equal(expect, sas.toString());
});
});
describe('#parse', function () {
/*Tests_SRS_NODE_IOTHUB_SAS_05_001: [The parse method shall return the result of calling azure-iot-common.SharedAccessSignature.parse.]*/
/*Tests_SRS_NODE_IOTHUB_SAS_05_002: [It shall throw ArgumentError if any of 'sr', 'sig', 'skn' or 'se' fields are not found in the source argument.]*/
['sr', 'sig', 'skn', 'se'].forEach(function (key) {
it('throws if shared access signature is missing ' + key, function () {
assert.throws(function () {
SharedAccessSignature.parse(incompleteSignatures[key]);
}, ArgumentError);
});
});
});
});

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше