-
Notifications
You must be signed in to change notification settings - Fork 24
/
client.js
368 lines (368 loc) · 25.1 KB
/
client.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
// 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 __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var events_1 = require("events");
var azure_iot_common_1 = require("azure-iot-common");
var azure_iot_common_2 = require("azure-iot-common");
var ConnectionString = require("./connection_string");
var amqp_1 = require("./amqp");
var device_method_1 = require("./device_method");
var azure_iot_http_base_1 = require("azure-iot-http-base");
// tslint:disable-next-line:no-var-requires
var packageJson = require('../package.json');
var 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 device-to-cloud (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.
*/
var Client = (function (_super) {
__extends(Client, _super);
/**
* @private
*/
function Client(transport, restApiClient) {
var _this = _super.call(this) || this;
/*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;
/*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 azure_iot_common_2.ExponentialBackOffWithJitter();
return _this;
}
/**
* @method module:azure-iothub.Client#open
* @description Opens the connection to an IoT hub.
* @param {Function} done The 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.
*/
Client.prototype.open = function (done) {
var _this = this;
/*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.]*/
var retryOp = new azure_iot_common_2.RetryOperation(this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry(function (retryCallback) {
_this._transport.connect(retryCallback);
}, function (err, result) {
if (err) {
if (done)
done(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 (done)
done(null, result);
}
});
};
/**
* @method module:azure-iothub.Client#close
* @description Closes the connection to an IoT hub.
* @param {Function} done The 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.
*/
Client.prototype.close = function (done) {
var _this = this;
/*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 done 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 done callback shall be invoked immediately with null arguments.]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_005: [The `close` method should not throw if the `done` callback is not specified.]*/
this._transport.disconnect(function (err, result) {
if (err) {
if (done)
done(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 (done)
done(null, result);
}
});
};
/**
* @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 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.
*
* @throws {ReferenceError} If `deviceId` or `message` is null, undefined or empty.
*/
Client.prototype.send = function (deviceId, message, done) {
var _this = this;
/*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 necessary.]*/
if (message.constructor.name !== 'Message') {
message = new azure_iot_common_1.Message(message);
}
/*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 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]*/
/*Codes_SRS_NODE_IOTHUB_CLIENT_05_017: [The argument err passed to the callback done 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.]*/
var retryOp = new azure_iot_common_2.RetryOperation(this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry(function (retryCallback) {
_this._transport.send(deviceId, message, retryCallback);
}, function (err, result) {
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_030: [The `send` method shall not throw if the `done` callback is falsy.]*/
if (done) {
if (err) {
done(err);
}
else {
done(null, result);
}
}
});
};
/**
* @method module:azure-iothub.Client#invokeDeviceMethod
* @description Invokes a method on a particular device.
* @param {String} deviceId The identifier of an existing device identity.
* @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 {Function} done The callback to call with the result of the method execution.
*
* @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.
*/
Client.prototype.invokeDeviceMethod = function (deviceId, methodParams, done) {
/*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 + '\'');
/*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.]*/
var method = new device_method_1.DeviceMethod(methodParams, 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.]*/
var retryOp = new azure_iot_common_2.RetryOperation(this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry(function (retryCallback) {
method.invokeOn(deviceId, retryCallback);
}, function (err, result, response) {
if (done) {
if (err) {
done(err);
}
else {
done(null, result, response);
}
}
});
};
/**
* @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 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.
*/
Client.prototype.getFeedbackReceiver = function (done) {
var _this = this;
/*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.]*/
var retryOp = new azure_iot_common_2.RetryOperation(this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry(function (retryCallback) {
_this._transport.getFeedbackReceiver(retryCallback);
}, function (err, result) {
if (done) {
if (err) {
done(err);
}
else {
done(null, result);
}
}
});
};
/**
* @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 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.
*/
Client.prototype.getFileNotificationReceiver = function (done) {
var _this = this;
/*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.]*/
var retryOp = new azure_iot_common_2.RetryOperation(this._retryPolicy, MAX_RETRY_TIMEOUT);
retryOp.retry(function (retryCallback) {
_this._transport.getFileNotificationReceiver(retryCallback);
}, function (err, result) {
if (done) {
if (err) {
done(err);
}
else {
done(null, result);
}
}
});
};
/**
* 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.
*/
Client.prototype.setRetryPolicy = function (policy) {
/*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 azure_iot_common_1.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;
};
Client.prototype._disconnectHandler = function (reason) {
/*Codes_SRS_NODE_IOTHUB_CLIENT_16_004: [** The `disconnect` event shall be emitted when the client is disconnected from the server.]*/
var evt = new azure_iot_common_1.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}
*/
Client.fromConnectionString = function (connStr, transportCtor) {
/*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_1.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.]*/
var cn = ConnectionString.parse(connStr);
var config = {
host: cn.HostName,
keyName: cn.SharedAccessKeyName,
sharedAccessSignature: azure_iot_common_1.SharedAccessSignature.create(cn.HostName, cn.SharedAccessKeyName, cn.SharedAccessKey, azure_iot_common_1.anHourFromNow())
};
/*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 azure_iot_http_base_1.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}
*/
Client.fromSharedAccessSignature = function (sharedAccessSignature, transportCtor) {
/*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_1.Amqp;
}
var sas = azure_iot_common_1.SharedAccessSignature.parse(sharedAccessSignature);
var 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.]*/
var config = {
host: decodedUri,
keyName: sas.skn,
sharedAccessSignature: sas.toString()
};
/*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 azure_iot_http_base_1.RestApiClient(config, packageJson.name + '/' + packageJson.version));
};
return Client;
}(events_1.EventEmitter));
exports.Client = Client;
//# sourceMappingURL=client.js.map