@itentialopensource/adapter-utils 4.49.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,730 @@
1
+ /* @copyright Itential, LLC 2023 */
2
+
3
+ // Set globals
4
+ /* global log */
5
+ /* eslint global-require:warn */
6
+ /* eslint import/no-dynamic-require:warn */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ /* NodeJS internal utilities */
12
+
13
+ let adapterDir = '';
14
+ let id = '';
15
+
16
+ /*
17
+ * INTERNAL FUNCTION: update device broker properties from service instance config and adapter sample properties
18
+ */
19
+ function getDeviceBrokerArray(allProps) {
20
+ const origin = `${id}-brokerHandler-getDeviceBrokerArray`;
21
+ log.trace(origin);
22
+ const brokerCallsArr = ['getDevice', 'getDevicesFiltered', 'isAlive', 'getConfig', 'getCount'];
23
+ const deviceBrokerProps = allProps.devicebroker;
24
+
25
+ try {
26
+ const sampleFile = path.join(adapterDir, '/sampleProperties.json');
27
+ let sampleProps = null;
28
+ if (fs.existsSync(sampleFile)) {
29
+ sampleProps = require(path.resolve(adapterDir, 'sampleProperties.json')).properties;
30
+ }
31
+
32
+ // go through all the device broker calls to get the call information
33
+ for (let i = 0; i < brokerCallsArr.length; i += 1) {
34
+ // If in service config that is primary - e.g do nothing
35
+ if (!allProps.devicebroker || !allProps.devicebroker[brokerCallsArr[i]] || allProps.devicebroker[brokerCallsArr[i]].length === 0 || !allProps.devicebroker[brokerCallsArr[i]][0].path) {
36
+ // if not in service config check sample props
37
+ if (!sampleProps.devicebroker || !sampleProps.devicebroker[brokerCallsArr[i]] || sampleProps.devicebroker[brokerCallsArr[i]].length === 0 || !sampleProps.devicebroker[brokerCallsArr[i]][0].path) {
38
+ // not in sample properties, nothing we can do
39
+ log.info(`No device broker props found for ${brokerCallsArr[i]}`);
40
+ deviceBrokerProps[brokerCallsArr[i]] = [];
41
+ } else {
42
+ // use the sample properties information
43
+ log.info(`Updating device broker props for ${brokerCallsArr[i]} from sample props`);
44
+ deviceBrokerProps[brokerCallsArr[i]] = sampleProps.devicebroker[brokerCallsArr[i]];
45
+ }
46
+ }
47
+ }
48
+
49
+ log.debug('Device broker array', JSON.stringify(deviceBrokerProps, null, 3));
50
+ return deviceBrokerProps;
51
+ } catch (ex) {
52
+ return {};
53
+ }
54
+ }
55
+
56
+ class BrokerHandler {
57
+ /**
58
+ * Adapter Broker Handler
59
+ * @constructor
60
+ */
61
+ constructor(prongId, properties, directory, reqH) {
62
+ this.myid = prongId;
63
+ id = prongId;
64
+ this.allProps = properties;
65
+ adapterDir = directory;
66
+ this.requestHandlerInst = reqH;
67
+
68
+ // set up the properties I care about
69
+ this.refreshProperties(properties);
70
+ }
71
+
72
+ /**
73
+ * refreshProperties is used to set up all of the properties for the broker handler.
74
+ * It allows properties to be changed later by simply calling refreshProperties rather
75
+ * than having to restart the broker handler.
76
+ *
77
+ * @function refreshProperties
78
+ * @param {Object} properties - an object containing all of the properties
79
+ */
80
+ refreshProperties(properties) {
81
+ const origin = `${this.myid}-brokerHandler-refreshProperties`;
82
+ log.trace(origin);
83
+
84
+ if (!properties) {
85
+ log.error(`${origin}: Broker Handler received no properties!`);
86
+ return;
87
+ }
88
+
89
+ // update deviceBrokerProperties
90
+ this.allProps.devicebroker = getDeviceBrokerArray(properties);
91
+
92
+ // this.
93
+ }
94
+
95
+ /**
96
+ * @summary Determines if this adapter supports any in a list of entities
97
+ *
98
+ * @function hasEntities
99
+ * @param {String} entityType - the entity type to check for
100
+ * @param {Array} entityList - the list of entities we are looking for
101
+ *
102
+ * @param {Callback} callback - A map where the entity is the key and the
103
+ * value is true or false
104
+ */
105
+ hasEntities(entityType, entityList, callback) {
106
+ const origin = `${this.myid}-brokerHandler-hasEntities`;
107
+ log.trace(origin);
108
+
109
+ switch (entityType) {
110
+ case 'Device':
111
+ return this.hasDevices(entityList, callback);
112
+ default:
113
+ return callback(null, `${this.myid} does not support entity ${entityType}`);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * @summary Helper method for hasEntities for the specific device case
119
+ *
120
+ * @param {Array} deviceList - array of unique device identifiers, name
121
+ * @param {Callback} callback - A map where the device is the key and the
122
+ * value is true or false
123
+ */
124
+ hasDevices(deviceList, callback) {
125
+ const origin = `${this.myid}-brokerHandler-hasDevices`;
126
+ log.trace(origin);
127
+
128
+ let findings = {}; // map
129
+
130
+ if (this.allProps.cache.enabled) { // a bit redundant but just in case errors in cacheHandler
131
+ let entityTypeCached = false;
132
+ this.requestHandlerInst.checkEntityTypeCached('Device', (res, error) => {
133
+ if (error) {
134
+ log.error(`${origin}: cache check failed with error ${error}`);
135
+ // continue as if cache disabled, "try again"
136
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, origin, error, null, null, null);
137
+ return callback(null, errorObj);
138
+ }
139
+
140
+ if (res) {
141
+ entityTypeCached = true;
142
+ // EntityType is cached. Retrieve devices from cache
143
+ return this.requestHandlerInst.checkEntityCached('Device', deviceList, (results, err) => {
144
+ if (err) {
145
+ return callback(null, { code: 503, message: 'Unable to do device lookup.', err });
146
+ }
147
+ for (let i = 0; i < deviceList.length; i += 1) {
148
+ if (results[i] === 'found') {
149
+ findings[deviceList[i]] = true;
150
+ } else { // notfound
151
+ findings[deviceList[i]] = false;
152
+ }
153
+ }
154
+ log.debug(`FINDINGS: ${JSON.stringify(findings)}`);
155
+ return callback(findings);
156
+ });
157
+ }
158
+ return null; // entity type not cached, exit callback and continue as if cache not enabled
159
+ });
160
+ if (entityTypeCached) {
161
+ return null; // called callback earlier, exit function
162
+ }
163
+ }
164
+
165
+ // manual - no cache
166
+ findings = deviceList.reduce((map, device) => {
167
+ // eslint-disable-next-line no-param-reassign
168
+ map[device] = false;
169
+ log.debug(`In reduce: ${JSON.stringify(map)}`);
170
+ return map;
171
+ }, {});
172
+ const apiCalls = deviceList.map((device) => new Promise((resolve) => {
173
+ this.getDevice(device, (result, error) => {
174
+ if (error) {
175
+ log.debug(`In map error: ${JSON.stringify(device)}`);
176
+ return resolve({ name: device, found: false });
177
+ }
178
+ log.debug(`In map: ${JSON.stringify(device)}`);
179
+ return resolve({ name: device, found: true });
180
+ });
181
+ }));
182
+ return Promise.all(apiCalls).then((results) => {
183
+ results.forEach((device) => {
184
+ findings[device.name] = device.found;
185
+ });
186
+ log.debug(`FINDINGS: ${JSON.stringify(findings)}`);
187
+ log.debug('FINDINGS: ', findings);
188
+ return callback(findings);
189
+ }).catch((errors) => {
190
+ log.error('Unable to do device lookup.');
191
+ return callback(null, { code: 503, message: 'Unable to do device lookup.', error: errors });
192
+ });
193
+ }
194
+
195
+ /**
196
+ * @summary Get Appliance that match the deviceName
197
+ *
198
+ * @function getDevice
199
+ * @param {String} deviceName - the deviceName to find (required)
200
+ *
201
+ * @param {getCallback} callback - a callback function to return the result
202
+ * (appliance) or the error
203
+ */
204
+ getDevice(deviceName, callback) {
205
+ const meth = 'brokerHandler-getDevice';
206
+ const origin = `${this.myid}-${meth}`;
207
+ log.trace(origin);
208
+
209
+ // make sure we are set up for device broker getDevice
210
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.getDevice || this.allProps.devicebroker.getDevice.length === 0 || !this.allProps.devicebroker.getDevice[0].path) {
211
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Properties devicebroker.getDevice.path', null, null, null, null);
212
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
213
+ return callback(null, errorObj);
214
+ }
215
+
216
+ /* HERE IS WHERE YOU VALIDATE DATA */
217
+ if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
218
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Data', ['deviceName'], null, null, null);
219
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
220
+ return callback(null, errorObj);
221
+ }
222
+
223
+ try {
224
+ // need to get the device so we can convert the deviceName to an id
225
+ // !! if we can do a lookup by name the getDevicesFiltered may not be necessary
226
+ const opts = {
227
+ filter: {
228
+ name: deviceName
229
+ }
230
+ };
231
+
232
+ return this.getDevicesFiltered(opts, (devs, ferr) => {
233
+ // if we received an error or their is no response on the results return an error
234
+ if (ferr) {
235
+ return callback(null, ferr);
236
+ }
237
+ if (devs.list.length < 1) {
238
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
239
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
240
+ return callback(null, errorObj);
241
+ }
242
+
243
+ const callPromises = [];
244
+ for (let i = 0; i < this.allProps.devicebroker.getDevice.length; i += 1) {
245
+ // Perform component calls here.
246
+ callPromises.push(
247
+ new Promise((resolve, reject) => {
248
+ this.requestHandlerInst.iapMakeGenericCall('getDevice', this.allProps.devicebroker.getDevice[i], [devs.list[0]], [deviceName], (callRet, callErr) => {
249
+ // return an error
250
+ if (callErr) {
251
+ reject(callErr);
252
+ } else {
253
+ // return the data
254
+ resolve(callRet);
255
+ }
256
+ });
257
+ })
258
+ );
259
+ }
260
+
261
+ // return an array of repsonses
262
+ return Promise.all(callPromises).then((results) => {
263
+ let myResult = {};
264
+ results.forEach((result) => {
265
+ myResult = { ...myResult, ...result };
266
+ });
267
+
268
+ return callback(myResult, null);
269
+ }, (error) => {
270
+ // return the error
271
+ log.error(`Call to Get Device Failed ${JSON.stringify(error)}`);
272
+ return callback(null, error);
273
+ });
274
+ });
275
+ } catch (ex) {
276
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Caught Exception', null, null, null, ex);
277
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
278
+ return callback(null, errorObj);
279
+ }
280
+ }
281
+
282
+ /**
283
+ * @summary Get Appliances that match the filter
284
+ *
285
+ * @function getDevicesFiltered
286
+ * @param {Object} options - the data to use to filter the appliances (optional)
287
+ *
288
+ * @param {getCallback} callback - a callback function to return the result
289
+ * (appliances) or the error
290
+ */
291
+ getDevicesFiltered(options, callback) {
292
+ const meth = 'brokerHandler-getDevicesFiltered';
293
+ const origin = `${this.myid}-${meth}`;
294
+ log.trace(origin);
295
+
296
+ // make sure we are set up for device broker getDevicesFiltered
297
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.getDevicesFiltered || this.allProps.devicebroker.getDevicesFiltered.length === 0 || !this.allProps.devicebroker.getDevicesFiltered[0].path) {
298
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Properties devicebroker.getDevicesFiltered.path', null, null, null);
299
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
300
+ return callback(null, errorObj);
301
+ }
302
+
303
+ // verify the required fields have been provided
304
+ if (options === undefined || options === null || options === '' || options.length === 0) {
305
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Data', ['options'], null, null, null);
306
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
307
+ return callback(null, errorObj);
308
+ }
309
+ log.debug(`Device Filter Options: ${JSON.stringify(options)}`);
310
+
311
+ if (!(options.start === undefined || options.start === null) && (options.limit === undefined || options.limit === null)) {
312
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Cannot specify start without limit.', null, null, null);
313
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
314
+ return callback(null, errorObj);
315
+ }
316
+
317
+ if ((options.start === undefined || options.start === null) && !(options.limit === undefined || options.limit === null)) {
318
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Cannot specify limit without start.', null, null, null);
319
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
320
+ return callback(null, errorObj);
321
+ }
322
+
323
+ if (typeof options.start === 'string' || typeof options.limit === 'string') {
324
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Start and Limit must be numbers.', null, null, null);
325
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
326
+ return callback(null, errorObj);
327
+ }
328
+
329
+ if (options.start < 0 || options.limit <= 0) {
330
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Limit and/or Start value is too low.', null, null, null);
331
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
332
+ return callback(null, errorObj);
333
+ }
334
+
335
+ // if caching get data from the cache
336
+ if (this.allProps.cache.enabled) { // a bit redundant but just in case errors in cacheHandler
337
+ let entityTypeCached = false;
338
+ this.requestHandlerInst.checkEntityTypeCached('Device', (res, error) => {
339
+ if (error) {
340
+ log.error(`${origin}: cache check failed with error ${error}`);
341
+ // continue as if cache disabled, "try again"
342
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, error, null, null, null);
343
+ return callback(null, errorObj);
344
+ }
345
+
346
+ if (res) {
347
+ entityTypeCached = true;
348
+ // Retrieve devices from cache
349
+ return this.requestHandlerInst.retrieveEntitiesCache('Device', options, (callRet, callErr) => {
350
+ if (callErr) {
351
+ log.error(callErr);
352
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, callErr, null, null, null);
353
+ return callback(null, errorObj);
354
+ }
355
+ // return the result
356
+ return callback({ total: callRet.length, list: callRet });
357
+ });
358
+ }
359
+ return null; // entity type not cached, exit callback and continue as if cache not enabled
360
+ });
361
+ if (entityTypeCached) {
362
+ return null; // called callback earlier, exit function
363
+ }
364
+ }
365
+
366
+ try {
367
+ // const nextToken = options.start;
368
+ // const maxResults = options.limit;
369
+
370
+ // set up the filter of Device Names
371
+ let filterName = [];
372
+ if (options && options.filter && options.filter.name) {
373
+ // when this hack is removed, remove the lint ignore above
374
+ if (Array.isArray(options.filter.name)) {
375
+ // eslint-disable-next-line prefer-destructuring
376
+ filterName = options.filter.name;
377
+ } else {
378
+ filterName = [options.filter.name];
379
+ }
380
+ }
381
+
382
+ const callPromises = [];
383
+ for (let i = 0; i < this.allProps.devicebroker.getDevicesFiltered.length; i += 1) {
384
+ // Perform component calls here.
385
+ callPromises.push(
386
+ new Promise((resolve, reject) => {
387
+ this.requestHandlerInst.iapMakeGenericCall('getDevicesFiltered', this.allProps.devicebroker.getDevicesFiltered[i], [{ fake: 'fakedata' }], filterName, (callRet, callErr) => {
388
+ // return an error
389
+ if (callErr) {
390
+ console.debug(`in cache iap call failed with error ${callErr}`);
391
+ reject(callErr);
392
+ } else {
393
+ // return the data
394
+ resolve(callRet);
395
+ }
396
+ });
397
+ })
398
+ );
399
+ }
400
+
401
+ // return an array of repsonses
402
+ return Promise.all(callPromises).then((results) => {
403
+ let myResult = [];
404
+ results.forEach((result) => {
405
+ if (Array.isArray(result)) {
406
+ myResult = [...myResult, ...result];
407
+ } else if (Object.keys(result).length > 0) {
408
+ myResult.push(result);
409
+ }
410
+ });
411
+
412
+ // sort by name property
413
+ if (options && options.sort) {
414
+ myResult.sort((a, b) => {
415
+ if (a.name > b.name) {
416
+ return 1;
417
+ }
418
+ if (a.name < b.name) {
419
+ return -1;
420
+ }
421
+ return 0;
422
+ });
423
+ }
424
+
425
+ // pagination: get entities limit*start to limit*(start+1) - 1
426
+ if (myResult.length !== 0 && options && options.start >= 0 && options.limit > 0) {
427
+ const end = Math.min(myResult.length, options.limit * (options.start + 1));
428
+ if (options.limit * options.start >= end) {
429
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Page number too large for limit size.', null, null, null);
430
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
431
+ return callback(null, errorObj);
432
+ }
433
+ myResult = myResult.slice(options.limit * options.start, end);
434
+ }
435
+ log.debug(`${origin}: Found #${myResult.length} devices.`);
436
+ log.debug(`Devices: ${JSON.stringify(myResult)}`);
437
+ return callback({ total: myResult.length, list: myResult });
438
+ }, (error) => {
439
+ // return the error
440
+ log.error(`Call to get devices Failed: ${JSON.stringify(error)}`);
441
+ return callback(null, error);
442
+ });
443
+ } catch (ex) {
444
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
445
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
446
+ return callback(null, errorObj);
447
+ }
448
+ }
449
+
450
+ /**
451
+ * @summary Gets the status for the provided appliance
452
+ *
453
+ * @function isAlive
454
+ * @param {String} deviceName - the deviceName of the appliance. (required)
455
+ *
456
+ * @param {configCallback} callback - callback function to return the result
457
+ * (appliance isAlive) or the error
458
+ */
459
+ isAlive(deviceName, callback) {
460
+ const meth = 'brokerHandler-isAlive';
461
+ const origin = `${this.myid}-${meth}`;
462
+ log.trace(origin);
463
+
464
+ // make sure we are set up for device broker isAlive
465
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.isAlive || this.allProps.devicebroker.isAlive.length === 0 || !this.allProps.devicebroker.isAlive[0].path) {
466
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Properties devicebroker.isAlive.path', null, null, null);
467
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
468
+ return callback(null, errorObj);
469
+ }
470
+
471
+ // verify the required fields have been provided
472
+ if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
473
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Data', ['deviceName'], null, null, null);
474
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
475
+ return callback(null, errorObj);
476
+ }
477
+
478
+ try {
479
+ // need to get the device so we can convert the deviceName to an id
480
+ // !! if we can do a lookup by name the getDevicesFiltered may not be necessary
481
+ const opts = {
482
+ filter: {
483
+ name: deviceName
484
+ }
485
+ };
486
+ return this.getDevicesFiltered(opts, (devs, ferr) => {
487
+ // if we received an error or their is no response on the results return an error
488
+ if (ferr) {
489
+ return callback(null, ferr);
490
+ }
491
+ if (devs.list.length < 1) {
492
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
493
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
494
+ return callback(null, errorObj);
495
+ }
496
+
497
+ const callPromises = [];
498
+ for (let i = 0; i < this.allProps.devicebroker.isAlive.length; i += 1) {
499
+ // Perform component calls here.
500
+ callPromises.push(
501
+ new Promise((resolve, reject) => {
502
+ this.requestHandlerInst.iapMakeGenericCall('isAlive', this.allProps.devicebroker.isAlive[i], [devs.list[0]], null, (callRet, callErr) => {
503
+ // return an error
504
+ if (callErr) {
505
+ reject(callErr);
506
+ } else {
507
+ // return the data
508
+ resolve(callRet);
509
+ }
510
+ });
511
+ })
512
+ );
513
+ }
514
+
515
+ // return an array of repsonses
516
+ return Promise.all(callPromises).then((results) => {
517
+ let myResult = {};
518
+ results.forEach((result) => {
519
+ myResult = { ...myResult, ...result };
520
+ });
521
+
522
+ let response = true;
523
+ if (myResult.isAlive !== null && myResult.isAlive !== undefined && myResult.isAlive === false) {
524
+ response = false;
525
+ }
526
+ return callback(response);
527
+ }, (error) => {
528
+ // return the error
529
+ log.error(`Call to check device status failed: ${JSON.stringify(error)}`);
530
+ return callback(null, error);
531
+ });
532
+ });
533
+ } catch (ex) {
534
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Caught Exception', null, null, null, ex);
535
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
536
+ return callback(null, errorObj);
537
+ }
538
+ }
539
+
540
+ /**
541
+ * @summary Gets a config for the provided Appliance
542
+ *
543
+ * @function getConfig
544
+ * @param {String} deviceName - the deviceName of the appliance. (required)
545
+ * @param {String} format - the desired format of the config. (optional)
546
+ *
547
+ * @param {configCallback} callback - callback function to return the result
548
+ * (appliance config) or the error
549
+ */
550
+ getConfig(deviceName, format, callback) {
551
+ const meth = 'brokerHandler-getConfig';
552
+ const origin = `${this.myid}-${meth}`;
553
+ log.trace(origin);
554
+
555
+ // make sure we are set up for device broker getConfig
556
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.getConfig || this.allProps.devicebroker.getConfig.length === 0 || !this.allProps.devicebroker.getConfig[0].path) {
557
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Properties devicebroker.getConfig.path', null, null, null);
558
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
559
+ return callback(null, errorObj);
560
+ }
561
+
562
+ // verify the required fields have been provided
563
+ if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
564
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Data', ['deviceName'], null, null, null);
565
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
566
+ return callback(null, errorObj);
567
+ }
568
+
569
+ try {
570
+ // need to get the device so we can convert the deviceName to an id
571
+ // !! if we can do a lookup by name the getDevicesFiltered may not be necessary
572
+ const opts = {
573
+ filter: {
574
+ name: deviceName
575
+ }
576
+ };
577
+ return this.getDevicesFiltered(opts, (devs, ferr) => {
578
+ // if we received an error or their is no response on the results return an error
579
+ if (ferr) {
580
+ return callback(null, ferr);
581
+ }
582
+ if (devs.list.length < 1) {
583
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
584
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
585
+ return callback(null, errorObj);
586
+ }
587
+
588
+ const callPromises = [];
589
+ for (let i = 0; i < this.allProps.devicebroker.getConfig.length; i += 1) {
590
+ // Perform component calls here.
591
+ callPromises.push(
592
+ new Promise((resolve, reject) => {
593
+ this.requestHandlerInst.iapMakeGenericCall('getConfig', this.allProps.devicebroker.getConfig[i], [devs.list[0]], null, (callRet, callErr) => {
594
+ // return an error
595
+ if (callErr) {
596
+ reject(callErr);
597
+ } else {
598
+ // return the data
599
+ resolve(callRet);
600
+ }
601
+ });
602
+ })
603
+ );
604
+ }
605
+
606
+ // return an array of repsonses
607
+ return Promise.all(callPromises).then((results) => {
608
+ let myResult = {};
609
+ results.forEach((result) => {
610
+ if (typeof result === 'string') {
611
+ myResult = { ...myResult, result };
612
+ } else if (Array.isArray(result)) {
613
+ // myResult = result[0]; todo Commented out to resolve lint error, unsure how to fix
614
+ } else {
615
+ myResult = { ...myResult, ...result };
616
+ }
617
+ });
618
+
619
+ // return the result
620
+ const newResponse = {
621
+ response: JSON.stringify(myResult, null, 2)
622
+ };
623
+ return callback(newResponse, null);
624
+ }, (error) => {
625
+ // return the error
626
+ log.error(`Call to Get Config Failed: ${JSON.stringify(error)}`);
627
+ return callback(null, error);
628
+ });
629
+ });
630
+ } catch (ex) {
631
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Caught Exception', null, null, null, ex);
632
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
633
+ return callback(null, errorObj);
634
+ }
635
+ }
636
+
637
+ /**
638
+ * @summary Gets the device count from the system
639
+ *
640
+ * @function iapGetDeviceCount
641
+ *
642
+ * @param {getCallback} callback - callback function to return the result
643
+ * (count) or the error
644
+ */
645
+ iapGetDeviceCount(callback) {
646
+ const meth = 'brokerHandler-iapGetDeviceCount';
647
+ const origin = `${this.myid}-${meth}`;
648
+ log.trace(origin);
649
+
650
+ // if caching get data from the cache
651
+ if (this.allProps.cache.enabled) { // a bit redundant but just in case errors in cacheHandler
652
+ let entityTypeCached = false;
653
+ this.requestHandlerInst.checkEntityTypeCached('Device', (res, error) => {
654
+ if (error) {
655
+ // should never reach here
656
+ log.error(`${origin}: cache check failed with error ${error}`);
657
+ // continue as if cache disabled, "try again"
658
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, error, null, null, null);
659
+ return callback(null, errorObj);
660
+ }
661
+
662
+ if (res) {
663
+ entityTypeCached = true;
664
+ // Retrieve devices from cache
665
+ return this.requestHandlerInst.retrieveEntitiesCache('Device', {}, (callRet, callErr) => {
666
+ if (callErr) {
667
+ // MAY WANT TO FORMAT AN ERROR AND RETURN THAT (null, error)
668
+ return callback({ count: -1 });
669
+ }
670
+ // return the result
671
+ return callback({ count: callRet.length });
672
+ });
673
+ }
674
+ return null; // if res === false then continue as if cache not enabled
675
+ });
676
+ if (entityTypeCached) {
677
+ return null; // called callback earlier
678
+ }
679
+ }
680
+
681
+ // make sure we are set up for device broker getCount
682
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.getCount || this.allProps.devicebroker.getCount.length === 0 || !this.allProps.devicebroker.getCount[0].path) {
683
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Missing Properties devicebroker.getCount.path', null, null, null);
684
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
685
+ return callback(null, errorObj);
686
+ }
687
+
688
+ // verify the required fields have been provided
689
+ try {
690
+ const callPromises = [];
691
+ for (let i = 0; i < this.allProps.devicebroker.getCount.length; i += 1) {
692
+ // Perform component calls here.
693
+ callPromises.push(
694
+ new Promise((resolve, reject) => {
695
+ this.requestHandlerInst.iapMakeGenericCall('getCount', this.allProps.devicebroker.getCount[i], [{ fake: 'fakedata' }], null, (callRet, callErr) => {
696
+ // return an error
697
+ if (callErr) {
698
+ reject(callErr);
699
+ } else {
700
+ // return the data
701
+ resolve(callRet);
702
+ }
703
+ });
704
+ })
705
+ );
706
+ }
707
+
708
+ // return an array of repsonses
709
+ return Promise.all(callPromises).then((results) => {
710
+ let myResult = {};
711
+ results.forEach((result) => {
712
+ myResult = { ...myResult, ...result };
713
+ });
714
+
715
+ // return the result
716
+ return callback({ count: Object.keys(myResult).length });
717
+ }, (error) => {
718
+ // return the error
719
+ log.error(`Call to Get Device Count Failed: ${JSON.stringify(error)}`);
720
+ return callback(null, error);
721
+ });
722
+ } catch (ex) {
723
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Caught Exception', null, null, null, ex);
724
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
725
+ return callback(null, errorObj);
726
+ }
727
+ }
728
+ }
729
+
730
+ module.exports = BrokerHandler;