@itentialopensource/adapter-microsoft_graph 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. package/.eslintignore +6 -0
  2. package/.eslintrc.js +18 -0
  3. package/.gitlab/.gitkeep +0 -0
  4. package/.gitlab/issue_templates/.gitkeep +0 -0
  5. package/.gitlab/issue_templates/Default.md +17 -0
  6. package/.gitlab/issue_templates/bugReportTemplate.md +42 -0
  7. package/.gitlab/issue_templates/featureRequestTemplate.md +14 -0
  8. package/.jshintrc +0 -0
  9. package/AUTH.md +39 -0
  10. package/BROKER.md +199 -0
  11. package/CALLS.md +170 -0
  12. package/CHANGELOG.md +9 -0
  13. package/CODE_OF_CONDUCT.md +43 -0
  14. package/CONTRIBUTING.md +172 -0
  15. package/ENHANCE.md +69 -0
  16. package/LICENSE +201 -0
  17. package/PROPERTIES.md +641 -0
  18. package/README.md +337 -0
  19. package/SUMMARY.md +9 -0
  20. package/SYSTEMINFO.md +11 -0
  21. package/TROUBLESHOOT.md +47 -0
  22. package/adapter.js +18798 -0
  23. package/adapterBase.js +1787 -0
  24. package/entities/.generic/action.json +214 -0
  25. package/entities/.generic/schema.json +28 -0
  26. package/entities/.system/action.json +50 -0
  27. package/entities/.system/mockdatafiles/getToken-default.json +3 -0
  28. package/entities/.system/mockdatafiles/healthcheck-default.json +3 -0
  29. package/entities/.system/schema.json +19 -0
  30. package/entities/.system/schemaTokenReq.json +53 -0
  31. package/entities/.system/schemaTokenResp.json +53 -0
  32. package/entities/Applications/action.json +127 -0
  33. package/entities/Applications/schema.json +35 -0
  34. package/entities/AzureADDevice/action.json +106 -0
  35. package/entities/AzureADDevice/schema.json +45 -0
  36. package/entities/Batch/action.json +24 -0
  37. package/entities/Batch/schema.json +19 -0
  38. package/entities/CaseCreation/action.json +249 -0
  39. package/entities/CaseCreation/schema.json +30 -0
  40. package/entities/Catalog/action.json +25 -0
  41. package/entities/Catalog/schema.json +19 -0
  42. package/entities/ConnectionSetup/action.json +148 -0
  43. package/entities/ConnectionSetup/schema.json +25 -0
  44. package/entities/ContentSync/action.json +65 -0
  45. package/entities/ContentSync/schema.json +21 -0
  46. package/entities/CreateDSR/action.json +45 -0
  47. package/entities/CreateDSR/schema.json +20 -0
  48. package/entities/Deployment/action.json +106 -0
  49. package/entities/Deployment/schema.json +34 -0
  50. package/entities/DeploymentAudience/action.json +66 -0
  51. package/entities/DeploymentAudience/schema.json +32 -0
  52. package/entities/Events/action.json +108 -0
  53. package/entities/Events/schema.json +34 -0
  54. package/entities/Files/action.json +108 -0
  55. package/entities/Files/schema.json +23 -0
  56. package/entities/Groups/action.json +25 -0
  57. package/entities/Groups/schema.json +19 -0
  58. package/entities/IdentitySync/action.json +105 -0
  59. package/entities/IdentitySync/schema.json +23 -0
  60. package/entities/Insights/action.json +46 -0
  61. package/entities/Insights/schema.json +20 -0
  62. package/entities/LabelManagement/action.json +106 -0
  63. package/entities/LabelManagement/schema.json +23 -0
  64. package/entities/Mail/action.json +212 -0
  65. package/entities/Mail/schema.json +72 -0
  66. package/entities/Memberships/action.json +167 -0
  67. package/entities/Memberships/schema.json +70 -0
  68. package/entities/Misc/action.json +66 -0
  69. package/entities/Misc/schema.json +21 -0
  70. package/entities/Notebooks/action.json +107 -0
  71. package/entities/Notebooks/schema.json +34 -0
  72. package/entities/OpenExtensions/action.json +65 -0
  73. package/entities/OpenExtensions/schema.json +54 -0
  74. package/entities/People/action.json +46 -0
  75. package/entities/People/schema.json +31 -0
  76. package/entities/SchemaExtensions/action.json +65 -0
  77. package/entities/SchemaExtensions/schema.json +32 -0
  78. package/entities/Search/action.json +24 -0
  79. package/entities/Search/schema.json +19 -0
  80. package/entities/Security/action.json +151 -0
  81. package/entities/Security/schema.json +58 -0
  82. package/entities/SharePoint/action.json +214 -0
  83. package/entities/SharePoint/schema.json +39 -0
  84. package/entities/Subscriptions/action.json +65 -0
  85. package/entities/Subscriptions/schema.json +32 -0
  86. package/entities/TasksPlanner/action.json +272 -0
  87. package/entities/TasksPlanner/schema.json +86 -0
  88. package/entities/TasksTodo/action.json +187 -0
  89. package/entities/TasksTodo/schema.json +49 -0
  90. package/entities/Teams/action.json +519 -0
  91. package/entities/Teams/schema.json +120 -0
  92. package/entities/TrackDSRStatus/action.json +108 -0
  93. package/entities/TrackDSRStatus/schema.json +23 -0
  94. package/entities/TriggerEventForExistingLabel/action.json +108 -0
  95. package/entities/TriggerEventForExistingLabel/schema.json +23 -0
  96. package/entities/Users/action.json +213 -0
  97. package/entities/Users/schema.json +50 -0
  98. package/entities/WorkflowAutomation/action.json +249 -0
  99. package/entities/WorkflowAutomation/schema.json +30 -0
  100. package/error.json +190 -0
  101. package/package.json +87 -0
  102. package/pronghorn.json +8654 -0
  103. package/propertiesDecorators.json +14 -0
  104. package/propertiesSchema.json +1248 -0
  105. package/refs?service=git-upload-pack +0 -0
  106. package/report/creationReport.json +1715 -0
  107. package/report/graph.json +14709 -0
  108. package/sampleProperties.json +195 -0
  109. package/test/integration/adapterTestBasicGet.js +83 -0
  110. package/test/integration/adapterTestConnectivity.js +93 -0
  111. package/test/integration/adapterTestIntegration.js +6059 -0
  112. package/test/unit/adapterBaseTestUnit.js +949 -0
  113. package/test/unit/adapterTestUnit.js +7492 -0
  114. package/utils/adapterInfo.js +206 -0
  115. package/utils/addAuth.js +94 -0
  116. package/utils/artifactize.js +146 -0
  117. package/utils/basicGet.js +50 -0
  118. package/utils/checkMigrate.js +63 -0
  119. package/utils/entitiesToDB.js +178 -0
  120. package/utils/findPath.js +74 -0
  121. package/utils/methodDocumentor.js +225 -0
  122. package/utils/modify.js +154 -0
  123. package/utils/packModificationScript.js +35 -0
  124. package/utils/patches2bundledDeps.js +90 -0
  125. package/utils/pre-commit.sh +32 -0
  126. package/utils/removeHooks.js +20 -0
  127. package/utils/setup.js +33 -0
  128. package/utils/tbScript.js +246 -0
  129. package/utils/tbUtils.js +490 -0
  130. package/utils/testRunner.js +298 -0
  131. package/utils/troubleshootingAdapter.js +195 -0
  132. package/workflows/README.md +3 -0
package/adapterBase.js ADDED
@@ -0,0 +1,1787 @@
1
+ /* @copyright Itential, LLC 2018-9 */
2
+
3
+ // Set globals
4
+ /* global log */
5
+ /* eslint class-methods-use-this:warn */
6
+ /* eslint import/no-dynamic-require: warn */
7
+ /* eslint no-loop-func: warn */
8
+ /* eslint no-cond-assign: warn */
9
+ /* eslint global-require: warn */
10
+ /* eslint no-unused-vars: warn */
11
+ /* eslint prefer-destructuring: warn */
12
+
13
+ /* Required libraries. */
14
+ const fs = require('fs-extra');
15
+ const path = require('path');
16
+ const jsonQuery = require('json-query');
17
+ const EventEmitterCl = require('events').EventEmitter;
18
+ const { execSync } = require('child_process');
19
+
20
+ /* The schema validator */
21
+ const AjvCl = require('ajv');
22
+
23
+ /* Fetch in the other needed components for the this Adaptor */
24
+ const PropUtilCl = require('@itentialopensource/adapter-utils').PropertyUtility;
25
+ const RequestHandlerCl = require('@itentialopensource/adapter-utils').RequestHandler;
26
+
27
+ const entitiesToDB = require(path.join(__dirname, 'utils/entitiesToDB'));
28
+ const troubleshootingAdapter = require(path.join(__dirname, 'utils/troubleshootingAdapter'));
29
+ const tbUtils = require(path.join(__dirname, 'utils/tbUtils'));
30
+
31
+ let propUtil = null;
32
+
33
+ /*
34
+ * INTERNAL FUNCTION: force fail the adapter - generally done to cause restart
35
+ */
36
+ function forceFail(packChg) {
37
+ if (packChg !== undefined && packChg !== null && packChg === true) {
38
+ execSync(`rm -rf ${__dirname}/node modules`, { encoding: 'utf-8' });
39
+ execSync(`rm -rf ${__dirname}/package-lock.json`, { encoding: 'utf-8' });
40
+ execSync('npm install', { encoding: 'utf-8' });
41
+ }
42
+ log.error('NEED TO RESTART ADAPTER - FORCE FAIL');
43
+ const errorObj = {
44
+ origin: 'adapter-forceFail',
45
+ type: 'Force Fail so adapter will restart',
46
+ vars: []
47
+ };
48
+ setTimeout(() => {
49
+ throw new Error(JSON.stringify(errorObj));
50
+ }, 1000);
51
+ }
52
+
53
+ /*
54
+ * INTERNAL FUNCTION: update the action.json
55
+ */
56
+ function updateAction(entityPath, action, changes) {
57
+ // if the action file does not exist - error
58
+ const actionFile = path.join(entityPath, '/action.json');
59
+ if (!fs.existsSync(actionFile)) {
60
+ return 'Missing Action File';
61
+ }
62
+
63
+ // read in the file as a json object
64
+ const ajson = require(path.resolve(entityPath, 'action.json'));
65
+ let chgAct = {};
66
+
67
+ // get the action we need to change
68
+ for (let a = 0; a < ajson.actions.length; a += 1) {
69
+ if (ajson.actions[a].name === action) {
70
+ chgAct = ajson.actions[a];
71
+ break;
72
+ }
73
+ }
74
+ // merge the changes into the desired action
75
+ chgAct = propUtil.mergeProperties(changes, chgAct);
76
+
77
+ fs.writeFileSync(actionFile, JSON.stringify(ajson, null, 2));
78
+ return null;
79
+ }
80
+
81
+ /*
82
+ * INTERNAL FUNCTION: update the schema file
83
+ */
84
+ function updateSchema(entityPath, configFile, changes) {
85
+ // if the schema file does not exist - error
86
+ const schemaFile = path.join(entityPath, `/${configFile}`);
87
+ if (!fs.existsSync(schemaFile)) {
88
+ return 'Missing Schema File';
89
+ }
90
+
91
+ // read in the file as a json object
92
+ let schema = require(path.resolve(entityPath, configFile));
93
+
94
+ // merge the changes into the schema file
95
+ schema = propUtil.mergeProperties(changes, schema);
96
+
97
+ fs.writeFileSync(schemaFile, JSON.stringify(schema, null, 2));
98
+ return null;
99
+ }
100
+
101
+ /*
102
+ * INTERNAL FUNCTION: update the mock data file
103
+ */
104
+ function updateMock(mockPath, configFile, changes) {
105
+ // if the mock file does not exist - create it
106
+ const mockFile = path.join(mockPath, `/${configFile}`);
107
+ if (!fs.existsSync(mockFile)) {
108
+ const newMock = {};
109
+ fs.writeFileSync(mockFile, JSON.stringify(newMock, null, 2));
110
+ }
111
+
112
+ // read in the file as a json object
113
+ let mock = require(path.resolve(mockPath, configFile));
114
+
115
+ // merge the changes into the mock file
116
+ mock = propUtil.mergeProperties(changes, mock);
117
+
118
+ fs.writeFileSync(mockFile, JSON.stringify(mock, null, 2));
119
+ return null;
120
+ }
121
+
122
+ /*
123
+ * INTERNAL FUNCTION: update the package dependencies
124
+ */
125
+ function updatePackage(changes) {
126
+ // if the schema file does not exist - error
127
+ const packFile = path.join(__dirname, '/package.json');
128
+ if (!fs.existsSync(packFile)) {
129
+ return 'Missing Pacakge File';
130
+ }
131
+
132
+ // read in the file as a json object
133
+ const pack = require(path.resolve(__dirname, 'package.json'));
134
+
135
+ // only certain changes are allowed
136
+ if (changes.dependencies) {
137
+ const keys = Object.keys(changes.dependencies);
138
+
139
+ for (let k = 0; k < keys.length; k += 1) {
140
+ pack.dependencies[keys[k]] = changes.dependencies[keys[k]];
141
+ }
142
+ }
143
+
144
+ fs.writeFileSync(packFile, JSON.stringify(pack, null, 2));
145
+ return null;
146
+ }
147
+
148
+ /*
149
+ * INTERNAL FUNCTION: get data from source(s) - nested
150
+ */
151
+ function getDataFromSources(loopField, sources) {
152
+ let fieldValue = loopField;
153
+
154
+ // go through the sources to find the field
155
+ for (let s = 0; s < sources.length; s += 1) {
156
+ // find the field value using jsonquery
157
+ const nestedValue = jsonQuery(loopField, { data: sources[s] }).value;
158
+
159
+ // if we found in source - set and no need to check other sources
160
+ if (nestedValue) {
161
+ fieldValue = nestedValue;
162
+ break;
163
+ }
164
+ }
165
+
166
+ return fieldValue;
167
+ }
168
+
169
+ /* GENERAL ADAPTER FUNCTIONS THESE SHOULD NOT BE DIRECTLY MODIFIED */
170
+ /* IF YOU NEED MODIFICATIONS, REDEFINE THEM IN adapter.js!!! */
171
+ class AdapterBase extends EventEmitterCl {
172
+ /**
173
+ * [System] Adapter
174
+ * @constructor
175
+ */
176
+ constructor(prongid, properties) {
177
+ // Instantiate the EventEmitter super class
178
+ super();
179
+
180
+ // IAP home directory injected by core when running the adapter within IAP
181
+ [, , , process.env.iap_home] = process.argv;
182
+
183
+ try {
184
+ // Capture the adapter id
185
+ this.id = prongid;
186
+ this.propUtilInst = new PropUtilCl(prongid, __dirname);
187
+ propUtil = this.propUtilInst;
188
+ this.initProps = properties;
189
+ this.alive = false;
190
+ this.healthy = false;
191
+ this.suspended = false;
192
+ this.suspendMode = 'pause';
193
+ this.caching = false;
194
+ this.repeatCacheCount = 0;
195
+ this.allowFailover = 'AD.300';
196
+ this.noFailover = 'AD.500';
197
+
198
+ // set up the properties I care about
199
+ this.refreshProperties(properties);
200
+
201
+ // Instantiate the other components for this Adapter
202
+ this.requestHandlerInst = new RequestHandlerCl(this.id, this.allProps, __dirname);
203
+ } catch (e) {
204
+ // handle any exception
205
+ const origin = `${this.id}-adapterBase-constructor`;
206
+ log.error(`${origin}: Adapter may not have started properly. ${e}`);
207
+ }
208
+ }
209
+
210
+ /**
211
+ * @callback healthCallback
212
+ * @param {Object} result - the result of the get request (contains an id and a status)
213
+ */
214
+ /**
215
+ * @callback getCallback
216
+ * @param {Object} result - the result of the get request (entity/ies)
217
+ * @param {String} error - any error that occured
218
+ */
219
+ /**
220
+ * @callback createCallback
221
+ * @param {Object} item - the newly created entity
222
+ * @param {String} error - any error that occured
223
+ */
224
+ /**
225
+ * @callback updateCallback
226
+ * @param {String} status - the status of the update action
227
+ * @param {String} error - any error that occured
228
+ */
229
+ /**
230
+ * @callback deleteCallback
231
+ * @param {String} status - the status of the delete action
232
+ * @param {String} error - any error that occured
233
+ */
234
+
235
+ /**
236
+ * refreshProperties is used to set up all of the properties for the connector.
237
+ * It allows properties to be changed later by simply calling refreshProperties rather
238
+ * than having to restart the connector.
239
+ *
240
+ * @function refreshProperties
241
+ * @param {Object} properties - an object containing all of the properties
242
+ * @param {boolean} init - are we initializing -- is so no need to refresh throtte engine
243
+ */
244
+ refreshProperties(properties) {
245
+ const meth = 'adapterBase-refreshProperties';
246
+ const origin = `${this.id}-${meth}`;
247
+ log.trace(origin);
248
+
249
+ try {
250
+ // Read the properties schema from the file system
251
+ const propertiesSchema = JSON.parse(fs.readFileSync(path.join(__dirname, 'propertiesSchema.json'), 'utf-8'));
252
+
253
+ // add any defaults to the data
254
+ const defProps = this.propUtilInst.setDefaults(propertiesSchema);
255
+ this.allProps = this.propUtilInst.mergeProperties(properties, defProps);
256
+
257
+ // validate the entity against the schema
258
+ const ajvInst = new AjvCl();
259
+ const validate = ajvInst.compile(propertiesSchema);
260
+ const result = validate(this.allProps);
261
+
262
+ // if invalid properties throw an error
263
+ if (!result) {
264
+ if (this.requestHandlerInst) {
265
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Invalid Properties', [JSON.stringify(validate.errors)], null, null, null);
266
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
267
+ throw new Error(JSON.stringify(errorObj));
268
+ } else {
269
+ log.error(`${origin}: ${JSON.stringify(validate.errors)}`);
270
+ throw new Error(`${origin}: ${JSON.stringify(validate.errors)}`);
271
+ }
272
+ }
273
+
274
+ // properties that this code cares about
275
+ this.healthcheckType = this.allProps.healthcheck.type;
276
+ this.healthcheckInterval = this.allProps.healthcheck.frequency;
277
+ this.healthcheckQuery = this.allProps.healthcheck.query_object;
278
+
279
+ // set the failover codes from properties
280
+ if (this.allProps.request.failover_codes) {
281
+ if (Array.isArray(this.allProps.request.failover_codes)) {
282
+ this.failoverCodes = this.allProps.request.failover_codes;
283
+ } else {
284
+ this.failoverCodes = [this.allProps.request.failover_codes];
285
+ }
286
+ } else {
287
+ this.failoverCodes = [];
288
+ }
289
+
290
+ // set the caching flag from properties
291
+ if (this.allProps.cache_location) {
292
+ if (this.allProps.cache_location === 'redis' || this.allProps.cache_location === 'local') {
293
+ this.caching = true;
294
+ }
295
+ }
296
+
297
+ // if this is truly a refresh and we have a request handler, refresh it
298
+ if (this.requestHandlerInst) {
299
+ this.requestHandlerInst.refreshProperties(properties);
300
+ }
301
+ } catch (e) {
302
+ log.error(`${origin}: Properties may not have been set properly. ${e}`);
303
+ }
304
+ }
305
+
306
+ /**
307
+ * @summary Connect function is used during Pronghorn startup to provide instantiation feedback.
308
+ *
309
+ * @function connect
310
+ */
311
+ connect() {
312
+ const origin = `${this.id}-adapterBase-connect`;
313
+ log.trace(origin);
314
+
315
+ // initially set as off
316
+ this.emit('OFFLINE', { id: this.id });
317
+ this.alive = true;
318
+
319
+ // if there is no healthcheck just change the emit to ONLINE
320
+ // We do not recommend no healthcheck!!!
321
+ if (this.healthcheckType === 'none') {
322
+ log.error(`${origin}: Waiting 1 Seconds to emit Online`);
323
+ setTimeout(() => {
324
+ this.emit('ONLINE', { id: this.id });
325
+ this.healthy = true;
326
+ }, 1000);
327
+ }
328
+
329
+ // is the healthcheck only suppose to run on startup
330
+ // (intermittent runs on startup and after that)
331
+ if (this.healthcheckType === 'startup' || this.healthcheckType === 'intermittent') {
332
+ // run an initial healthcheck
333
+ this.healthCheck(null, (status) => {
334
+ log.spam(`${origin}: ${status}`);
335
+ });
336
+ }
337
+
338
+ // is the healthcheck suppose to run intermittently
339
+ if (this.healthcheckType === 'intermittent') {
340
+ // run the healthcheck in an interval
341
+ setInterval(() => {
342
+ // try to see if mongo is available
343
+ this.healthCheck(null, (status) => {
344
+ log.spam(`${origin}: ${status}`);
345
+ });
346
+ }, this.healthcheckInterval);
347
+ }
348
+ }
349
+
350
+ /**
351
+ * @summary HealthCheck function is used to provide Pronghorn the status of this adapter.
352
+ *
353
+ * @function healthCheck
354
+ */
355
+ healthCheck(reqObj, callback) {
356
+ const origin = `${this.id}-adapterBase-healthCheck`;
357
+ log.trace(origin);
358
+
359
+ // if there is healthcheck query_object property, it needs to be added to the adapter
360
+ let myRequest = reqObj;
361
+ if (this.healthcheckQuery && Object.keys(this.healthcheckQuery).length > 0) {
362
+ if (myRequest && myRequest.uriQuery) {
363
+ myRequest.uriQuery = { ...myRequest.uriQuery, ...this.healthcheckQuery };
364
+ } else if (myRequest) {
365
+ myRequest.uriQuery = this.healthcheckQuery;
366
+ } else {
367
+ myRequest = {
368
+ uriQuery: this.healthcheckQuery
369
+ };
370
+ }
371
+ }
372
+
373
+ // call to the healthcheck in connector
374
+ return this.requestHandlerInst.identifyHealthcheck(myRequest, (res, error) => {
375
+ // unhealthy
376
+ if (error) {
377
+ // if we were healthy, toggle health
378
+ if (this.healthy) {
379
+ this.emit('OFFLINE', { id: this.id });
380
+ this.emit('DEGRADED', { id: this.id });
381
+ this.healthy = false;
382
+ if (typeof error === 'object') {
383
+ log.error(`${origin}: HEALTH CHECK - Error ${JSON.stringify(error)}`);
384
+ } else {
385
+ log.error(`${origin}: HEALTH CHECK - Error ${error}`);
386
+ }
387
+ } else if (typeof error === 'object') {
388
+ // still log but set the level to trace
389
+ log.trace(`${origin}: HEALTH CHECK - Still Errors ${JSON.stringify(error)}`);
390
+ } else {
391
+ log.trace(`${origin}: HEALTH CHECK - Still Errors ${error}`);
392
+ }
393
+
394
+ return callback(false);
395
+ }
396
+
397
+ // if we were unhealthy, toggle health
398
+ if (!this.healthy) {
399
+ this.emit('FIXED', { id: this.id });
400
+ this.emit('ONLINE', { id: this.id });
401
+ this.healthy = true;
402
+ log.info(`${origin}: HEALTH CHECK SUCCESSFUL`);
403
+ } else {
404
+ // still log but set the level to trace
405
+ log.trace(`${origin}: HEALTH CHECK STILL SUCCESSFUL`);
406
+ }
407
+
408
+ return callback(true);
409
+ });
410
+ }
411
+
412
+ /**
413
+ * getAllFunctions is used to get all of the exposed function in the adapter
414
+ *
415
+ * @function getAllFunctions
416
+ */
417
+ getAllFunctions() {
418
+ let myfunctions = [];
419
+ let obj = this;
420
+
421
+ // find the functions in this class
422
+ do {
423
+ const l = Object.getOwnPropertyNames(obj)
424
+ .concat(Object.getOwnPropertySymbols(obj).map((s) => s.toString()))
425
+ .sort()
426
+ .filter((p, i, arr) => typeof obj[p] === 'function' && p !== 'constructor' && (i === 0 || p !== arr[i - 1]) && myfunctions.indexOf(p) === -1);
427
+ myfunctions = myfunctions.concat(l);
428
+ }
429
+ while (
430
+ (obj = Object.getPrototypeOf(obj)) && Object.getPrototypeOf(obj)
431
+ );
432
+
433
+ return myfunctions;
434
+ }
435
+
436
+ /**
437
+ * checkActionFiles is used to update the validation of the action files.
438
+ *
439
+ * @function checkActionFiles
440
+ */
441
+ checkActionFiles() {
442
+ const origin = `${this.id}-adapterBase-checkActionFiles`;
443
+ log.trace(origin);
444
+
445
+ // validate the action files for the adapter
446
+ try {
447
+ return this.requestHandlerInst.checkActionFiles();
448
+ } catch (e) {
449
+ return ['Exception increase log level'];
450
+ }
451
+ }
452
+
453
+ /**
454
+ * checkProperties is used to validate the adapter properties.
455
+ *
456
+ * @function checkProperties
457
+ * @param {Object} properties - an object containing all of the properties
458
+ */
459
+ checkProperties(properties) {
460
+ const origin = `${this.myid}-adapterBase-checkProperties`;
461
+ log.trace(origin);
462
+
463
+ // validate the properties for the adapter
464
+ try {
465
+ return this.requestHandlerInst.checkProperties(properties);
466
+ } catch (e) {
467
+ return { exception: 'Exception increase log level' };
468
+ }
469
+ }
470
+
471
+ /**
472
+ * @summary Takes in property text and an encoding/encryption and returns the resulting
473
+ * encoded/encrypted string
474
+ *
475
+ * @function encryptProperty
476
+ * @param {String} property - the property to encrypt
477
+ * @param {String} technique - the technique to use to encrypt
478
+ *
479
+ * @param {Callback} callback - a callback function to return the result
480
+ * Encrypted String or the Error
481
+ */
482
+ encryptProperty(property, technique, callback) {
483
+ const origin = `${this.id}-adapterBase-encryptProperty`;
484
+ log.trace(origin);
485
+
486
+ // Make the call -
487
+ // encryptProperty(property, technique, callback)
488
+ return this.requestHandlerInst.encryptProperty(property, technique, callback);
489
+ }
490
+
491
+ /**
492
+ * iapGetAdapterWorkflowFunctions is used to get all of the workflow function in the adapter
493
+ * @param {array} ignoreThese - additional methods to ignore (optional)
494
+ *
495
+ * @function iapGetAdapterWorkflowFunctions
496
+ */
497
+ iapGetAdapterWorkflowFunctions(ignoreThese) {
498
+ const myfunctions = this.getAllFunctions();
499
+ const wffunctions = [];
500
+
501
+ // remove the functions that should not be in a Workflow
502
+ for (let m = 0; m < myfunctions.length; m += 1) {
503
+ if (myfunctions[m] === 'addEntityCache') {
504
+ // got to the second tier (adapterBase)
505
+ break;
506
+ }
507
+ if (!(myfunctions[m].endsWith('Emit') || myfunctions[m].match(/Emit__v[0-9]+/))) {
508
+ let found = false;
509
+ if (ignoreThese && Array.isArray(ignoreThese)) {
510
+ for (let i = 0; i < ignoreThese.length; i += 1) {
511
+ if (myfunctions[m].toUpperCase() === ignoreThese[i].toUpperCase()) {
512
+ found = true;
513
+ }
514
+ }
515
+ }
516
+ if (!found) {
517
+ wffunctions.push(myfunctions[m]);
518
+ }
519
+ }
520
+ }
521
+
522
+ return wffunctions;
523
+ }
524
+
525
+ /**
526
+ * iapUpdateAdapterConfiguration is used to update any of the adapter configuration files. This
527
+ * allows customers to make changes to adapter configuration without having to be on the
528
+ * file system.
529
+ *
530
+ * @function iapUpdateAdapterConfiguration
531
+ * @param {string} configFile - the name of the file being updated (required)
532
+ * @param {Object} changes - an object containing all of the changes = formatted like the configuration file (required)
533
+ * @param {string} entity - the entity to be changed, if an action, schema or mock data file (optional)
534
+ * @param {string} type - the type of entity file to change, (action, schema, mock) (optional)
535
+ * @param {string} action - the action to be changed, if an action, schema or mock data file (optional)
536
+ * @param {Callback} callback - The results of the call
537
+ */
538
+ iapUpdateAdapterConfiguration(configFile, changes, entity, type, action, callback) {
539
+ const meth = 'adapterBase-iapUpdateAdapterConfiguration';
540
+ const origin = `${this.id}-${meth}`;
541
+ log.trace(origin);
542
+
543
+ // verify the parameters are valid
544
+ if (changes === undefined || changes === null || typeof changes !== 'object'
545
+ || Object.keys(changes).length === 0) {
546
+ const result = {
547
+ response: 'No configuration updates to make'
548
+ };
549
+ log.info(result.response);
550
+ return callback(result, null);
551
+ }
552
+ if (configFile === undefined || configFile === null || configFile === '') {
553
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['configFile'], null, null, null);
554
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
555
+ return callback(null, errorObj);
556
+ }
557
+
558
+ // take action based on configFile being changed
559
+ if (configFile === 'package.json') {
560
+ const pres = updatePackage(changes);
561
+ if (pres) {
562
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Incomplete Configuration Change: ${pres}`, [], null, null, null);
563
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
564
+ return callback(null, errorObj);
565
+ }
566
+ const result = {
567
+ response: 'Package updates completed - restarting adapter'
568
+ };
569
+ log.info(result.response);
570
+ forceFail(true);
571
+ return callback(result, null);
572
+ }
573
+ if (entity === undefined || entity === null || entity === '') {
574
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Unsupported Configuration Change or Missing Entity', [], null, null, null);
575
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
576
+ return callback(null, errorObj);
577
+ }
578
+
579
+ // this means we are changing an entity file so type is required
580
+ if (type === undefined || type === null || type === '') {
581
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['type'], null, null, null);
582
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
583
+ return callback(null, errorObj);
584
+ }
585
+
586
+ // if the entity does not exist - error
587
+ const epath = `${__dirname}/entities/${entity}`;
588
+ if (!fs.existsSync(epath)) {
589
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Incomplete Configuration Change: Invalid Entity - ${entity}`, [], null, null, null);
590
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
591
+ return callback(null, errorObj);
592
+ }
593
+
594
+ // take action based on type of file being changed
595
+ if (type === 'action') {
596
+ // BACKUP???
597
+ const ares = updateAction(epath, action, changes);
598
+ if (ares) {
599
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Incomplete Configuration Change: ${ares}`, [], null, null, null);
600
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
601
+ return callback(null, errorObj);
602
+ }
603
+ // AJV CHECK???
604
+ // RESTORE IF NEEDED???
605
+ const result = {
606
+ response: `Action updates completed to entity: ${entity} - ${action}`
607
+ };
608
+ log.info(result.response);
609
+ return callback(result, null);
610
+ }
611
+ if (type === 'schema') {
612
+ const sres = updateSchema(epath, configFile, changes);
613
+ if (sres) {
614
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Incomplete Configuration Change: ${sres}`, [], null, null, null);
615
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
616
+ return callback(null, errorObj);
617
+ }
618
+ const result = {
619
+ response: `Schema updates completed to entity: ${entity} - ${configFile}`
620
+ };
621
+ log.info(result.response);
622
+ return callback(result, null);
623
+ }
624
+ if (type === 'mock') {
625
+ // if the mock directory does not exist - error
626
+ const mpath = `${__dirname}/entities/${entity}/mockdatafiles`;
627
+ if (!fs.existsSync(mpath)) {
628
+ fs.mkdirSync(mpath);
629
+ }
630
+
631
+ const mres = updateMock(mpath, configFile, changes);
632
+ if (mres) {
633
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Incomplete Configuration Change: ${mres}`, [], null, null, null);
634
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
635
+ return callback(null, errorObj);
636
+ }
637
+ const result = {
638
+ response: `Mock data updates completed to entity: ${entity} - ${configFile}`
639
+ };
640
+ log.info(result.response);
641
+ return callback(result, null);
642
+ }
643
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Incomplete Configuration Change: Unsupported Type - ${type}`, [], null, null, null);
644
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
645
+ return callback(null, errorObj);
646
+ }
647
+
648
+ /**
649
+ * See if the API path provided is found in this adapter
650
+ *
651
+ * @function iapFindAdapterPath
652
+ * @param {string} apiPath - the api path to check on
653
+ * @param {Callback} callback - The results of the call
654
+ */
655
+ iapFindAdapterPath(apiPath, callback) {
656
+ const result = {
657
+ apiPath
658
+ };
659
+
660
+ // verify the path was provided
661
+ if (!apiPath) {
662
+ log.error('NO API PATH PROVIDED!');
663
+ result.found = false;
664
+ result.message = 'NO PATH PROVIDED!';
665
+ return callback(null, result);
666
+ }
667
+
668
+ // make sure the entities directory exists
669
+ const entitydir = path.join(__dirname, 'entities');
670
+ if (!fs.statSync(entitydir).isDirectory()) {
671
+ log.error('Could not find the entities directory');
672
+ result.found = false;
673
+ result.message = 'Could not find the entities directory';
674
+ return callback(null, result);
675
+ }
676
+
677
+ const entities = fs.readdirSync(entitydir);
678
+ const fitems = [];
679
+
680
+ // need to go through each entity in the entities directory
681
+ for (let e = 0; e < entities.length; e += 1) {
682
+ // make sure the entity is a directory - do not care about extra files
683
+ // only entities (dir)
684
+ if (fs.statSync(`${entitydir}/${entities[e]}`).isDirectory()) {
685
+ // see if the action file exists in the entity
686
+ if (fs.existsSync(`${entitydir}/${entities[e]}/action.json`)) {
687
+ // Read the entity actions from the file system
688
+ const actions = require(`${entitydir}/${entities[e]}/action.json`);
689
+
690
+ // go through all of the actions set the appropriate info in the newActions
691
+ for (let a = 0; a < actions.actions.length; a += 1) {
692
+ if (actions.actions[a].entitypath.indexOf(apiPath) >= 0) {
693
+ log.info(` Found - entity: ${entities[e]} action: ${actions.actions[a].name}`);
694
+ log.info(` method: ${actions.actions[a].method} path: ${actions.actions[a].entitypath}`);
695
+ const fitem = {
696
+ entity: entities[e],
697
+ action: actions.actions[a].name,
698
+ method: actions.actions[a].method,
699
+ path: actions.actions[a].entitypath
700
+ };
701
+ fitems.push(fitem);
702
+ }
703
+ }
704
+ } else {
705
+ log.error(`Could not find entities ${entities[e]} action.json file`);
706
+ result.found = false;
707
+ result.message = `Could not find entities ${entities[e]} action.json file`;
708
+ return callback(null, result);
709
+ }
710
+ } else {
711
+ log.error(`Could not find entities ${entities[e]} directory`);
712
+ result.found = false;
713
+ result.message = `Could not find entities ${entities[e]} directory`;
714
+ return callback(null, result);
715
+ }
716
+ }
717
+
718
+ if (fitems.length === 0) {
719
+ log.info('PATH NOT FOUND!');
720
+ result.found = false;
721
+ result.message = 'API PATH NOT FOUND!';
722
+ return callback(null, result);
723
+ }
724
+
725
+ result.foundIn = fitems;
726
+ result.found = true;
727
+ result.message = 'API PATH FOUND!';
728
+ return callback(result, null);
729
+ }
730
+
731
+ /**
732
+ * @summary Suspends the adapter
733
+ * @param {Callback} callback - The adapater suspension status
734
+ * @function iapSuspendAdapter
735
+ */
736
+ iapSuspendAdapter(mode, callback) {
737
+ const origin = `${this.id}-adapterBase-iapSuspendAdapter`;
738
+ if (this.suspended) {
739
+ throw new Error(`${origin}: Adapter is already suspended`);
740
+ }
741
+ try {
742
+ this.suspended = true;
743
+ this.suspendMode = mode;
744
+ if (this.suspendMode === 'pause') {
745
+ const props = JSON.parse(JSON.stringify(this.initProps));
746
+ // To suspend adapter, enable throttling and set concurrent max to 0
747
+ props.throttle.throttle_enabled = true;
748
+ props.throttle.concurrent_max = 0;
749
+ this.refreshProperties(props);
750
+ }
751
+ return callback({ suspended: true });
752
+ } catch (error) {
753
+ return callback(null, error);
754
+ }
755
+ }
756
+
757
+ /**
758
+ * @summary Unsuspends the adapter
759
+ * @param {Callback} callback - The adapater suspension status
760
+ *
761
+ * @function iapUnsuspendAdapter
762
+ */
763
+ iapUnsuspendAdapter(callback) {
764
+ const origin = `${this.id}-adapterBase-iapUnsuspendAdapter`;
765
+ if (!this.suspended) {
766
+ throw new Error(`${origin}: Adapter is not suspended`);
767
+ }
768
+ if (this.suspendMode === 'pause') {
769
+ const props = JSON.parse(JSON.stringify(this.initProps));
770
+ // To unsuspend adapter, keep throttling enabled and begin processing queued requests in order
771
+ props.throttle.throttle_enabled = true;
772
+ props.throttle.concurrent_max = 1;
773
+ this.refreshProperties(props);
774
+ setTimeout(() => {
775
+ this.getQueue((q, error) => {
776
+ // console.log("Items in queue: " + String(q.length))
777
+ if (q.length === 0) {
778
+ // if queue is empty, return to initial properties state
779
+ this.refreshProperties(this.initProps);
780
+ this.suspended = false;
781
+ return callback({ suspended: false });
782
+ }
783
+ // recursive call to check queue again every second
784
+ return this.iapUnsuspendAdapter(callback);
785
+ });
786
+ }, 1000);
787
+ } else {
788
+ this.suspended = false;
789
+ callback({ suspend: false });
790
+ }
791
+ }
792
+
793
+ /**
794
+ * iapGetAdapterQueue is used to get information for all of the requests currently in the queue.
795
+ *
796
+ * @function iapGetAdapterQueue
797
+ * @param {Callback} callback - a callback function to return the result (Queue) or the error
798
+ */
799
+ iapGetAdapterQueue(callback) {
800
+ const origin = `${this.id}-adapterBase-iapGetAdapterQueue`;
801
+ log.trace(origin);
802
+
803
+ return this.requestHandlerInst.getQueue(callback);
804
+ }
805
+
806
+ /**
807
+ * @summary runs troubleshoot scripts for adapter
808
+ *
809
+ * @function iapTroubleshootAdapter
810
+ * @param {Object} props - the connection, healthcheck and authentication properties
811
+ * @param {boolean} persistFlag - whether the adapter properties should be updated
812
+ * @param {Adapter} adapter - adapter instance to troubleshoot
813
+ * @param {Callback} callback - callback function to return troubleshoot results
814
+ */
815
+ async iapTroubleshootAdapter(props, persistFlag, adapter, callback) {
816
+ try {
817
+ const result = await troubleshootingAdapter.troubleshoot(props, false, persistFlag, adapter);
818
+ if (result.healthCheck && result.connectivity.failCount === 0 && result.basicGet.failCount === 0) {
819
+ return callback(result);
820
+ }
821
+ return callback(null, result);
822
+ } catch (error) {
823
+ return callback(null, error);
824
+ }
825
+ }
826
+
827
+ /**
828
+ * @summary runs healthcheck script for adapter
829
+ *
830
+ * @function iapRunAdapterHealthcheck
831
+ * @param {Adapter} adapter - adapter instance to troubleshoot
832
+ * @param {Callback} callback - callback function to return healthcheck status
833
+ */
834
+ async iapRunAdapterHealthcheck(adapter, callback) {
835
+ try {
836
+ const result = await tbUtils.healthCheck(adapter);
837
+ if (result) {
838
+ return callback(result);
839
+ }
840
+ return callback(null, 'Healthcheck failed');
841
+ } catch (error) {
842
+ return callback(null, error);
843
+ }
844
+ }
845
+
846
+ /**
847
+ * @summary runs connectivity check script for adapter
848
+ *
849
+ * @function iapRunAdapterConnectivity
850
+ * @param {Adapter} adapter - adapter instance to troubleshoot
851
+ * @param {Callback} callback - callback function to return connectivity status
852
+ */
853
+ async iapRunAdapterConnectivity(callback) {
854
+ try {
855
+ const { host } = this.allProps;
856
+ const result = tbUtils.runConnectivity(host, false);
857
+ if (result.failCount > 0) {
858
+ return callback(null, result);
859
+ }
860
+ return callback(result);
861
+ } catch (error) {
862
+ return callback(null, error);
863
+ }
864
+ }
865
+
866
+ /**
867
+ * @summary runs basicGet script for adapter
868
+ *
869
+ * @function iapRunAdapterBasicGet
870
+ * @param {Callback} callback - callback function to return basicGet result
871
+ */
872
+ iapRunAdapterBasicGet(callback) {
873
+ try {
874
+ const result = tbUtils.runBasicGet(false);
875
+ if (result.failCount > 0) {
876
+ return callback(null, result);
877
+ }
878
+ return callback(result);
879
+ } catch (error) {
880
+ return callback(null, error);
881
+ }
882
+ }
883
+
884
+ /**
885
+ * @summary moves entities to mongo database
886
+ *
887
+ * @function iapMoveAdapterEntitiesToDB
888
+ *
889
+ * @return {Callback} - containing the response from the mongo transaction
890
+ */
891
+ async iapMoveAdapterEntitiesToDB(callback) {
892
+ const meth = 'adapterBase-iapMoveAdapterEntitiesToDB';
893
+ const origin = `${this.id}-${meth}`;
894
+ log.trace(origin);
895
+
896
+ try {
897
+ const result = await entitiesToDB.moveEntitiesToDB(__dirname, { pronghornProps: this.allProps, id: this.id });
898
+ return callback(result, null);
899
+ } catch (err) {
900
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, err);
901
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
902
+ return callback(null, err.message);
903
+ }
904
+ }
905
+
906
+ /**
907
+ * @summary take the entities and add them to the cache
908
+ *
909
+ * @function addEntityCache
910
+ * @param {String} entityType - the type of the entities
911
+ * @param {Array} data - the list of entities
912
+ * @param {String} key - unique key for the entities
913
+ *
914
+ * @param {Callback} callback - An array of whether the adapter can has the
915
+ * desired capability or an error
916
+ */
917
+ addEntityCache(entityType, entities, key, callback) {
918
+ const meth = 'adapterBase-addEntityCache';
919
+ const origin = `${this.id}-${meth}`;
920
+ log.trace(origin);
921
+
922
+ // list containing the items to add to the cache
923
+ const entityIds = [];
924
+
925
+ if (entities && Object.hasOwnProperty.call(entities, 'response')
926
+ && Array.isArray(entities.response)) {
927
+ for (let e = 0; e < entities.response.length; e += 1) {
928
+ entityIds.push(entities.response[e][key]);
929
+ }
930
+ }
931
+
932
+ // add the entities to the cache
933
+ return this.requestHandlerInst.addEntityCache(entityType, entityIds, (loaded, error) => {
934
+ if (error) {
935
+ return callback(null, error);
936
+ }
937
+ if (!loaded) {
938
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Entity Cache Not Loading', [entityType], null, null, null);
939
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
940
+ return callback(null, errorObj);
941
+ }
942
+
943
+ return callback(loaded);
944
+ });
945
+ }
946
+
947
+ /**
948
+ * @summary sees if the entity is in the entity list or not
949
+ *
950
+ * @function entityInList
951
+ * @param {String/Array} entityId - the specific entity we are looking for
952
+ * @param {Array} data - the list of entities
953
+ *
954
+ * @param {Callback} callback - An array of whether the adapter can has the
955
+ * desired capability or an error
956
+ */
957
+ entityInList(entityId, data) {
958
+ const origin = `${this.id}-adapterBase-entityInList`;
959
+ log.trace(origin);
960
+
961
+ // need to check on the entities that were passed in
962
+ if (Array.isArray(entityId)) {
963
+ const resEntity = [];
964
+
965
+ for (let e = 0; e < entityId.length; e += 1) {
966
+ if (data.includes(entityId[e])) {
967
+ resEntity.push(true);
968
+ } else {
969
+ resEntity.push(false);
970
+ }
971
+ }
972
+
973
+ return resEntity;
974
+ }
975
+
976
+ // does the entity list include the specific entity
977
+ return [data.includes(entityId)];
978
+ }
979
+
980
+ /**
981
+ * @summary prepare results for verify capability so they are true/false
982
+ *
983
+ * @function capabilityResults
984
+ * @param {Array} results - the results from the capability check
985
+ *
986
+ * @param {Callback} callback - An array of whether the adapter can has the
987
+ * desired capability or an error
988
+ */
989
+ capabilityResults(results, callback) {
990
+ const meth = 'adapterBase-capabilityResults';
991
+ const origin = `${this.id}-${meth}`;
992
+ log.trace(origin);
993
+ let locResults = results;
994
+
995
+ if (locResults && locResults[0] === 'needupdate') {
996
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Entity Cache Not Loading', ['unknown'], null, null, null);
997
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
998
+ this.repeatCacheCount += 1;
999
+ return callback(null, errorObj);
1000
+ }
1001
+
1002
+ // if an error occured, return the error
1003
+ if (locResults && locResults[0] === 'error') {
1004
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Error Verifying Entity Cache', null, null, null, null);
1005
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1006
+ return callback(null, errorObj);
1007
+ }
1008
+
1009
+ // go through the response and change to true/false
1010
+ if (locResults) {
1011
+ // if not an array, just convert the return
1012
+ if (!Array.isArray(locResults)) {
1013
+ if (locResults === 'found') {
1014
+ locResults = [true];
1015
+ } else {
1016
+ locResults = [false];
1017
+ }
1018
+ } else {
1019
+ const temp = [];
1020
+
1021
+ // go through each element in the array to convert
1022
+ for (let r = 0; r < locResults.length; r += 1) {
1023
+ if (locResults[r] === 'found') {
1024
+ temp.push(true);
1025
+ } else {
1026
+ temp.push(false);
1027
+ }
1028
+ }
1029
+ locResults = temp;
1030
+ }
1031
+ }
1032
+
1033
+ // return the results
1034
+ return callback(locResults);
1035
+ }
1036
+
1037
+ /**
1038
+ * @summary Provides a way for the adapter to tell north bound integrations
1039
+ * all of the capabilities for the current adapter
1040
+ *
1041
+ * @function getAllCapabilities
1042
+ *
1043
+ * @return {Array} - containing the entities and the actions available on each entity
1044
+ */
1045
+ getAllCapabilities() {
1046
+ const origin = `${this.id}-adapterBase-getAllCapabilities`;
1047
+ log.trace(origin);
1048
+
1049
+ // validate the capabilities for the adapter
1050
+ try {
1051
+ return this.requestHandlerInst.getAllCapabilities();
1052
+ } catch (e) {
1053
+ return [];
1054
+ }
1055
+ }
1056
+
1057
+ /**
1058
+ * @summary Determines if this adapter supports any in a list of entities
1059
+ *
1060
+ * @function hasEntities
1061
+ * @param {String} entityType - the entity type to check for
1062
+ * @param {Array} entityList - the list of entities we are looking for
1063
+ *
1064
+ * @param {Callback} callback - A map where the entity is the key and the
1065
+ * value is true or false
1066
+ */
1067
+ hasEntities(entityType, entityList, callback) {
1068
+ const origin = `${this.id}-adapter-hasEntities`;
1069
+ log.trace(origin);
1070
+
1071
+ switch (entityType) {
1072
+ case 'Device':
1073
+ return this.hasDevices(entityList, callback);
1074
+ default:
1075
+ return callback(null, `${this.id} does not support entity ${entityType}`);
1076
+ }
1077
+ }
1078
+
1079
+ /**
1080
+ * @summary Helper method for hasEntities for the specific device case
1081
+ *
1082
+ * @param {Array} deviceList - array of unique device identifiers
1083
+ * @param {Callback} callback - A map where the device is the key and the
1084
+ * value is true or false
1085
+ */
1086
+ hasDevices(deviceList, callback) {
1087
+ const origin = `${this.id}-adapter-hasDevices`;
1088
+ log.trace(origin);
1089
+
1090
+ const findings = deviceList.reduce((map, device) => {
1091
+ // eslint-disable-next-line no-param-reassign
1092
+ map[device] = false;
1093
+ log.debug(`In reduce: ${JSON.stringify(map)}`);
1094
+ return map;
1095
+ }, {});
1096
+ const apiCalls = deviceList.map((device) => new Promise((resolve) => {
1097
+ this.getDevice(device, (result, error) => {
1098
+ if (error) {
1099
+ log.debug(`In map error: ${JSON.stringify(device)}`);
1100
+ return resolve({ name: device, found: false });
1101
+ }
1102
+ log.debug(`In map: ${JSON.stringify(device)}`);
1103
+ return resolve({ name: device, found: true });
1104
+ });
1105
+ }));
1106
+ Promise.all(apiCalls).then((results) => {
1107
+ results.forEach((device) => {
1108
+ findings[device.name] = device.found;
1109
+ });
1110
+ log.debug(`FINDINGS: ${JSON.stringify(findings)}`);
1111
+ return callback(findings);
1112
+ }).catch((errors) => {
1113
+ log.error('Unable to do device lookup.');
1114
+ return callback(null, { code: 503, message: 'Unable to do device lookup.', error: errors });
1115
+ });
1116
+ }
1117
+
1118
+ /**
1119
+ * @summary Make one of the needed Broker calls - could be one of many
1120
+ *
1121
+ * @function iapMakeBrokerCall
1122
+ * @param {string} brokCall - the name of the broker call (required)
1123
+ * @param {object} callProps - the proeprties for the broker call (required)
1124
+ * @param {object} devResp - the device details to extract needed inputs (required)
1125
+ * @param {string} filterName - any filter to search on (required)
1126
+ *
1127
+ * @param {getCallback} callback - a callback function to return the result of the call
1128
+ */
1129
+ iapMakeBrokerCall(brokCall, callProps, devResp, filterName, callback) {
1130
+ const meth = 'adapterBase-iapMakeBrokerCall';
1131
+ const origin = `${this.id}-${meth}`;
1132
+ log.trace(origin);
1133
+
1134
+ try {
1135
+ let uriPath = '';
1136
+ let uriMethod = 'GET';
1137
+ let callQuery = {};
1138
+ let callBody = {};
1139
+ let callHeaders = {};
1140
+ let handleFail = 'fail';
1141
+ let ostypePrefix = '';
1142
+ let statusValue = 'true';
1143
+ if (callProps.path) {
1144
+ uriPath = `${callProps.path}`;
1145
+
1146
+ // make any necessary changes to the path
1147
+ if (devResp !== null && callProps.requestFields && Object.keys(callProps.requestFields).length > 0) {
1148
+ const rqKeys = Object.keys(callProps.requestFields);
1149
+
1150
+ // get the field from the provided device
1151
+ for (let rq = 0; rq < rqKeys.length; rq += 1) {
1152
+ const fieldValue = getDataFromSources(callProps.requestFields[rqKeys[rq]], devResp);
1153
+
1154
+ // put the value into the path - if it has been specified in the path
1155
+ uriPath = uriPath.replace(`{${rqKeys[rq]}}`, fieldValue);
1156
+ }
1157
+ }
1158
+ }
1159
+ if (callProps.method) {
1160
+ uriMethod = callProps.method;
1161
+ }
1162
+ if (callProps.query) {
1163
+ callQuery = callProps.query;
1164
+
1165
+ // go through the query params to check for variable values
1166
+ const cpKeys = Object.keys(callQuery);
1167
+ for (let cp = 0; cp < cpKeys.length; cp += 1) {
1168
+ if (callQuery[cpKeys[cp]].startsWith('{') && callQuery[cpKeys[cp]].endsWith('}')) {
1169
+ // make any necessary changes to the query params
1170
+ if (devResp !== null && callProps.requestFields && Object.keys(callProps.requestFields).length > 0) {
1171
+ const rqKeys = Object.keys(callProps.requestFields);
1172
+
1173
+ // get the field from the provided device
1174
+ for (let rq = 0; rq < rqKeys.length; rq += 1) {
1175
+ if (cpKeys[cp] === rqKeys[rq]) {
1176
+ const fieldValue = getDataFromSources(callProps.requestFields[rqKeys[rq]], devResp);
1177
+
1178
+ // put the value into the query - if it has been specified in the query
1179
+ callQuery[cpKeys[cp]] = fieldValue;
1180
+ }
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+ }
1186
+ if (callProps.body) {
1187
+ callBody = callProps.body;
1188
+
1189
+ // go through the body fields to check for variable values
1190
+ const cbKeys = Object.keys(callBody);
1191
+ for (let cb = 0; cb < cbKeys.length; cb += 1) {
1192
+ if (callBody[cbKeys[cb]].startsWith('{') && callBody[cbKeys[cb]].endsWith('}')) {
1193
+ // make any necessary changes to the query params
1194
+ if (devResp !== null && callProps.requestFields && Object.keys(callProps.requestFields).length > 0) {
1195
+ const rqKeys = Object.keys(callProps.requestFields);
1196
+
1197
+ // get the field from the provided device
1198
+ for (let rq = 0; rq < rqKeys.length; rq += 1) {
1199
+ if (cbKeys[cb] === rqKeys[rq]) {
1200
+ const fieldValue = getDataFromSources(callProps.requestFields[rqKeys[rq]], devResp);
1201
+
1202
+ // put the value into the query - if it has been specified in the query
1203
+ callBody[cbKeys[cb]] = fieldValue;
1204
+ }
1205
+ }
1206
+ }
1207
+ }
1208
+ }
1209
+ }
1210
+ if (callProps.headers) {
1211
+ callHeaders = callProps.headers;
1212
+
1213
+ // go through the body fields to check for variable values
1214
+ const chKeys = Object.keys(callHeaders);
1215
+ for (let ch = 0; ch < chKeys.length; ch += 1) {
1216
+ if (callHeaders[chKeys[ch]].startsWith('{') && callHeaders[chKeys[ch]].endsWith('}')) {
1217
+ // make any necessary changes to the query params
1218
+ if (devResp !== null && callProps.requestFields && Object.keys(callProps.requestFields).length > 0) {
1219
+ const rqKeys = Object.keys(callProps.requestFields);
1220
+
1221
+ // get the field from the provided device
1222
+ for (let rq = 0; rq < rqKeys.length; rq += 1) {
1223
+ if (chKeys[ch] === rqKeys[rq]) {
1224
+ const fieldValue = getDataFromSources(callProps.requestFields[rqKeys[rq]], devResp);
1225
+
1226
+ // put the value into the query - if it has been specified in the query
1227
+ callHeaders[chKeys[ch]] = fieldValue;
1228
+ }
1229
+ }
1230
+ }
1231
+ }
1232
+ }
1233
+ }
1234
+ if (callProps.handleFailure) {
1235
+ handleFail = callProps.handleFailure;
1236
+ }
1237
+ if (callProps.responseFields && callProps.responseFields.ostypePrefix) {
1238
+ ostypePrefix = callProps.responseFields.ostypePrefix;
1239
+ }
1240
+ if (callProps.responseFields && callProps.responseFields.statusValue) {
1241
+ statusValue = callProps.responseFields.statusValue;
1242
+ }
1243
+
1244
+ // !! using Generic makes it easier on the Adapter Builder (just need to change the path)
1245
+ // !! you can also replace with a specific call if that is easier
1246
+ return this.genericAdapterRequest(uriPath, uriMethod, callQuery, callBody, callHeaders, (result, error) => {
1247
+ // if we received an error or their is no response on the results return an error
1248
+ if (error) {
1249
+ if (handleFail === 'fail') {
1250
+ return callback(null, error);
1251
+ }
1252
+ return callback({}, null);
1253
+ }
1254
+ if (!result.response) {
1255
+ if (handleFail === 'fail') {
1256
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Invalid Response', [brokCall], null, null, null);
1257
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1258
+ return callback(null, errorObj);
1259
+ }
1260
+ return callback({}, null);
1261
+ }
1262
+
1263
+ // get the response piece we care about from the response
1264
+ const myResult = result;
1265
+ if (callProps.responseDatakey) {
1266
+ myResult.response = jsonQuery(callProps.responseDatakey, { data: myResult.response }).value;
1267
+ }
1268
+
1269
+ // get the keys for the response fields
1270
+ let rfKeys = [];
1271
+ if (callProps.responseFields && Object.keys(callProps.responseFields).length > 0) {
1272
+ rfKeys = Object.keys(callProps.responseFields);
1273
+ }
1274
+
1275
+ // if we got an array returned (e.g. getDevicesFitered)
1276
+ if (Array.isArray(myResult.response)) {
1277
+ const listDevices = [];
1278
+ for (let a = 0; a < myResult.response.length; a += 1) {
1279
+ const thisDevice = myResult.response[a];
1280
+ for (let rf = 0; rf < rfKeys.length; rf += 1) {
1281
+ if (rfKeys[rf] !== 'ostypePrefix') {
1282
+ let fieldValue = getDataFromSources(callProps.responseFields[rfKeys[rf]], [thisDevice, devResp, callProps.requestFields]);
1283
+
1284
+ // if the field is ostype - need to add prefix
1285
+ if (rfKeys[rf] === 'ostype' && typeof fieldValue === 'string') {
1286
+ fieldValue = ostypePrefix + fieldValue;
1287
+ }
1288
+ // if there is a status to set, set it
1289
+ if (rfKeys[rf] === 'status') {
1290
+ // if really looking for just a good response
1291
+ if (callProps.responseFields[rfKeys[rf]] === 'return2xx' && myResult.icode === statusValue.toString()) {
1292
+ thisDevice.isAlive = true;
1293
+ } else if (fieldValue.toString() === statusValue.toString()) {
1294
+ thisDevice.isAlive = true;
1295
+ } else {
1296
+ thisDevice.isAlive = false;
1297
+ }
1298
+ }
1299
+ // if we found a good value
1300
+ thisDevice[rfKeys[rf]] = fieldValue;
1301
+ }
1302
+ }
1303
+
1304
+ // if there is no filter - add the device to the list
1305
+ if (!filterName || filterName.length === 0) {
1306
+ listDevices.push(thisDevice);
1307
+ } else {
1308
+ // if we have to match a filter
1309
+ let found = false;
1310
+ for (let f = 0; f < filterName.length; f += 1) {
1311
+ if (thisDevice.name.indexOf(filterName[f]) >= 0) {
1312
+ found = true;
1313
+ break;
1314
+ }
1315
+ }
1316
+ // matching device
1317
+ if (found) {
1318
+ listDevices.push(thisDevice);
1319
+ }
1320
+ }
1321
+ }
1322
+
1323
+ // return the array of devices
1324
+ return callback(listDevices, null);
1325
+ }
1326
+
1327
+ // if this is not an array - just about everything else, just handle as a single object
1328
+ let thisDevice = myResult.response;
1329
+ for (let rf = 0; rf < rfKeys.length; rf += 1) {
1330
+ // skip ostypePrefix since it is not a field
1331
+ if (rfKeys[rf] !== 'ostypePrefix') {
1332
+ let fieldValue = getDataFromSources(callProps.responseFields[rfKeys[rf]], [thisDevice, devResp, callProps.requestFields]);
1333
+
1334
+ // if the field is ostype - need to add prefix
1335
+ if (rfKeys[rf] === 'ostype' && typeof fieldValue === 'string') {
1336
+ fieldValue = ostypePrefix + fieldValue;
1337
+ }
1338
+ // if there is a status to set, set it
1339
+ if (rfKeys[rf] === 'status') {
1340
+ // if really looking for just a good response
1341
+ if (callProps.responseFields[rfKeys[rf]] === 'return2xx' && myResult.icode === statusValue.toString()) {
1342
+ thisDevice.isAlive = true;
1343
+ } else if (fieldValue.toString() === statusValue.toString()) {
1344
+ thisDevice.isAlive = true;
1345
+ } else {
1346
+ thisDevice.isAlive = false;
1347
+ }
1348
+ }
1349
+ // if we found a good value
1350
+ thisDevice[rfKeys[rf]] = fieldValue;
1351
+ }
1352
+ }
1353
+
1354
+ // if there is a filter - check the device is in the list
1355
+ if (filterName && filterName.length > 0) {
1356
+ let found = false;
1357
+ for (let f = 0; f < filterName.length; f += 1) {
1358
+ if (thisDevice.name.indexOf(filterName[f]) >= 0) {
1359
+ found = true;
1360
+ break;
1361
+ }
1362
+ }
1363
+ // no matching device - clear the device
1364
+ if (!found) {
1365
+ thisDevice = {};
1366
+ }
1367
+ }
1368
+
1369
+ return callback(thisDevice, null);
1370
+ });
1371
+ } catch (e) {
1372
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, e);
1373
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1374
+ return callback(null, errorObj);
1375
+ }
1376
+ }
1377
+
1378
+ /**
1379
+ * @summary Get Appliance that match the deviceName
1380
+ *
1381
+ * @function getDevice
1382
+ * @param {String} deviceName - the deviceName to find (required)
1383
+ *
1384
+ * @param {getCallback} callback - a callback function to return the result
1385
+ * (appliance) or the error
1386
+ */
1387
+ getDevice(deviceName, callback) {
1388
+ const meth = 'adapterBase-getDevice';
1389
+ const origin = `${this.id}-${meth}`;
1390
+ log.trace(origin);
1391
+
1392
+ // make sure we are set up for device broker getDevice
1393
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.getDevice || this.allProps.devicebroker.getDevice.length === 0 || !this.allProps.devicebroker.getDevice[0].path) {
1394
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Properties', ['devicebroker.getDevice.path'], null, null, null);
1395
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1396
+ return callback(null, errorObj);
1397
+ }
1398
+
1399
+ /* HERE IS WHERE YOU VALIDATE DATA */
1400
+ if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
1401
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['deviceName'], null, null, null);
1402
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1403
+ return callback(null, errorObj);
1404
+ }
1405
+
1406
+ try {
1407
+ // need to get the device so we can convert the deviceName to an id
1408
+ // !! if we can do a lookup by name the getDevicesFiltered may not be necessary
1409
+ const opts = {
1410
+ filter: {
1411
+ name: deviceName
1412
+ }
1413
+ };
1414
+ return this.getDevicesFiltered(opts, (devs, ferr) => {
1415
+ // if we received an error or their is no response on the results return an error
1416
+ if (ferr) {
1417
+ return callback(null, ferr);
1418
+ }
1419
+ if (devs.list.length < 1) {
1420
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
1421
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1422
+ return callback(null, errorObj);
1423
+ }
1424
+
1425
+ const callPromises = [];
1426
+ for (let i = 0; i < this.allProps.devicebroker.getDevice.length; i += 1) {
1427
+ // Perform component calls here.
1428
+ callPromises.push(
1429
+ new Promise((resolve, reject) => {
1430
+ this.iapMakeBrokerCall('getDevice', this.allProps.devicebroker.getDevice[i], [devs.list[0]], null, (callRet, callErr) => {
1431
+ // return an error
1432
+ if (callErr) {
1433
+ reject(callErr);
1434
+ } else {
1435
+ // return the data
1436
+ resolve(callRet);
1437
+ }
1438
+ });
1439
+ })
1440
+ );
1441
+ }
1442
+
1443
+ // return an array of repsonses
1444
+ return Promise.all(callPromises).then((results) => {
1445
+ let myResult = {};
1446
+ results.forEach((result) => {
1447
+ myResult = { ...myResult, ...result };
1448
+ });
1449
+
1450
+ return callback(myResult, null);
1451
+ });
1452
+ });
1453
+ } catch (ex) {
1454
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
1455
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1456
+ return callback(null, errorObj);
1457
+ }
1458
+ }
1459
+
1460
+ /**
1461
+ * @summary Get Appliances that match the filter
1462
+ *
1463
+ * @function getDevicesFiltered
1464
+ * @param {Object} options - the data to use to filter the appliances (optional)
1465
+ *
1466
+ * @param {getCallback} callback - a callback function to return the result
1467
+ * (appliances) or the error
1468
+ */
1469
+ getDevicesFiltered(options, callback) {
1470
+ const meth = 'adapterBase-getDevicesFiltered';
1471
+ const origin = `${this.id}-${meth}`;
1472
+ log.trace(origin);
1473
+
1474
+ // make sure we are set up for device broker getDevicesFiltered
1475
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.getDevicesFiltered || this.allProps.devicebroker.getDevicesFiltered.length === 0 || !this.allProps.devicebroker.getDevicesFiltered[0].path) {
1476
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Properties', ['devicebroker.getDevicesFiltered.path'], null, null, null);
1477
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1478
+ return callback(null, errorObj);
1479
+ }
1480
+
1481
+ // verify the required fields have been provided
1482
+ if (options === undefined || options === null || options === '' || options.length === 0) {
1483
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['options'], null, null, null);
1484
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1485
+ return callback(null, errorObj);
1486
+ }
1487
+ log.debug(`Device Filter Options: ${JSON.stringify(options)}`);
1488
+
1489
+ try {
1490
+ // TODO - get pagination working
1491
+ // const nextToken = options.start;
1492
+ // const maxResults = options.limit;
1493
+
1494
+ // set up the filter of Device Names
1495
+ let filterName = [];
1496
+ if (options && options.filter && options.filter.name) {
1497
+ // when this hack is removed, remove the lint ignore above
1498
+ if (Array.isArray(options.filter.name)) {
1499
+ // eslint-disable-next-line prefer-destructuring
1500
+ filterName = options.filter.name;
1501
+ } else {
1502
+ filterName = [options.filter.name];
1503
+ }
1504
+ }
1505
+
1506
+ // TODO - get sort and order working
1507
+ /*
1508
+ if (options && options.sort) {
1509
+ reqObj.uriOptions.sort = JSON.stringify(options.sort);
1510
+ }
1511
+ if (options && options.order) {
1512
+ reqObj.uriOptions.order = options.order;
1513
+ }
1514
+ */
1515
+ const callPromises = [];
1516
+ for (let i = 0; i < this.allProps.devicebroker.getDevicesFiltered.length; i += 1) {
1517
+ // Perform component calls here.
1518
+ callPromises.push(
1519
+ new Promise((resolve, reject) => {
1520
+ this.iapMakeBrokerCall('getDevicesFiltered', this.allProps.devicebroker.getDevicesFiltered[i], [{ fake: 'fakedata' }], filterName, (callRet, callErr) => {
1521
+ // return an error
1522
+ if (callErr) {
1523
+ reject(callErr);
1524
+ } else {
1525
+ // return the data
1526
+ resolve(callRet);
1527
+ }
1528
+ });
1529
+ })
1530
+ );
1531
+ }
1532
+
1533
+ // return an array of repsonses
1534
+ return Promise.all(callPromises).then((results) => {
1535
+ let myResult = [];
1536
+ results.forEach((result) => {
1537
+ if (Array.isArray(result)) {
1538
+ myResult = [...myResult, ...result];
1539
+ } else if (Object.keys(result).length > 0) {
1540
+ myResult.push(result);
1541
+ }
1542
+ });
1543
+
1544
+ log.debug(`${origin}: Found #${myResult.length} devices.`);
1545
+ log.debug(`Devices: ${JSON.stringify(myResult)}`);
1546
+ return callback({ total: myResult.length, list: myResult });
1547
+ });
1548
+ } catch (ex) {
1549
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
1550
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1551
+ return callback(null, errorObj);
1552
+ }
1553
+ }
1554
+
1555
+ /**
1556
+ * @summary Gets the status for the provided appliance
1557
+ *
1558
+ * @function isAlive
1559
+ * @param {String} deviceName - the deviceName of the appliance. (required)
1560
+ *
1561
+ * @param {configCallback} callback - callback function to return the result
1562
+ * (appliance isAlive) or the error
1563
+ */
1564
+ isAlive(deviceName, callback) {
1565
+ const meth = 'adapterBase-isAlive';
1566
+ const origin = `${this.id}-${meth}`;
1567
+ log.trace(origin);
1568
+
1569
+ // make sure we are set up for device broker isAlive
1570
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.isAlive || this.allProps.devicebroker.isAlive.length === 0 || !this.allProps.devicebroker.isAlive[0].path) {
1571
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Properties', ['devicebroker.isAlive.path'], null, null, null);
1572
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1573
+ return callback(null, errorObj);
1574
+ }
1575
+
1576
+ // verify the required fields have been provided
1577
+ if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
1578
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['deviceName'], null, null, null);
1579
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1580
+ return callback(null, errorObj);
1581
+ }
1582
+
1583
+ try {
1584
+ // need to get the device so we can convert the deviceName to an id
1585
+ // !! if we can do a lookup by name the getDevicesFiltered may not be necessary
1586
+ const opts = {
1587
+ filter: {
1588
+ name: deviceName
1589
+ }
1590
+ };
1591
+ return this.getDevicesFiltered(opts, (devs, ferr) => {
1592
+ // if we received an error or their is no response on the results return an error
1593
+ if (ferr) {
1594
+ return callback(null, ferr);
1595
+ }
1596
+ if (devs.list.length < 1) {
1597
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
1598
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1599
+ return callback(null, errorObj);
1600
+ }
1601
+
1602
+ const callPromises = [];
1603
+ for (let i = 0; i < this.allProps.devicebroker.isAlive.length; i += 1) {
1604
+ // Perform component calls here.
1605
+ callPromises.push(
1606
+ new Promise((resolve, reject) => {
1607
+ this.iapMakeBrokerCall('isAlive', this.allProps.devicebroker.isAlive[i], [devs.list[0]], null, (callRet, callErr) => {
1608
+ // return an error
1609
+ if (callErr) {
1610
+ reject(callErr);
1611
+ } else {
1612
+ // return the data
1613
+ resolve(callRet);
1614
+ }
1615
+ });
1616
+ })
1617
+ );
1618
+ }
1619
+
1620
+ // return an array of repsonses
1621
+ return Promise.all(callPromises).then((results) => {
1622
+ let myResult = {};
1623
+ results.forEach((result) => {
1624
+ myResult = { ...myResult, ...result };
1625
+ });
1626
+
1627
+ let response = true;
1628
+ if (myResult.isAlive !== null && myResult.isAlive !== undefined && myResult.isAlive === false) {
1629
+ response = false;
1630
+ }
1631
+ return callback(response);
1632
+ });
1633
+ });
1634
+ } catch (ex) {
1635
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
1636
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1637
+ return callback(null, errorObj);
1638
+ }
1639
+ }
1640
+
1641
+ /**
1642
+ * @summary Gets a config for the provided Appliance
1643
+ *
1644
+ * @function getConfig
1645
+ * @param {String} deviceName - the deviceName of the appliance. (required)
1646
+ * @param {String} format - the desired format of the config. (optional)
1647
+ *
1648
+ * @param {configCallback} callback - callback function to return the result
1649
+ * (appliance config) or the error
1650
+ */
1651
+ getConfig(deviceName, format, callback) {
1652
+ const meth = 'adapterBase-getConfig';
1653
+ const origin = `${this.id}-${meth}`;
1654
+ log.trace(origin);
1655
+
1656
+ // make sure we are set up for device broker getConfig
1657
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.getConfig || this.allProps.devicebroker.getConfig.length === 0 || !this.allProps.devicebroker.getConfig[0].path) {
1658
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Properties', ['devicebroker.getConfig.path'], null, null, null);
1659
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1660
+ return callback(null, errorObj);
1661
+ }
1662
+
1663
+ // verify the required fields have been provided
1664
+ if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
1665
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['deviceName'], null, null, null);
1666
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1667
+ return callback(null, errorObj);
1668
+ }
1669
+
1670
+ try {
1671
+ // need to get the device so we can convert the deviceName to an id
1672
+ // !! if we can do a lookup by name the getDevicesFiltered may not be necessary
1673
+ const opts = {
1674
+ filter: {
1675
+ name: deviceName
1676
+ }
1677
+ };
1678
+ return this.getDevicesFiltered(opts, (devs, ferr) => {
1679
+ // if we received an error or their is no response on the results return an error
1680
+ if (ferr) {
1681
+ return callback(null, ferr);
1682
+ }
1683
+ if (devs.list.length < 1) {
1684
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
1685
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1686
+ return callback(null, errorObj);
1687
+ }
1688
+
1689
+ const callPromises = [];
1690
+ for (let i = 0; i < this.allProps.devicebroker.getConfig.length; i += 1) {
1691
+ // Perform component calls here.
1692
+ callPromises.push(
1693
+ new Promise((resolve, reject) => {
1694
+ this.iapMakeBrokerCall('getConfig', this.allProps.devicebroker.getConfig[i], [devs.list[0]], null, (callRet, callErr) => {
1695
+ // return an error
1696
+ if (callErr) {
1697
+ reject(callErr);
1698
+ } else {
1699
+ // return the data
1700
+ resolve(callRet);
1701
+ }
1702
+ });
1703
+ })
1704
+ );
1705
+ }
1706
+
1707
+ // return an array of repsonses
1708
+ return Promise.all(callPromises).then((results) => {
1709
+ let myResult = {};
1710
+ results.forEach((result) => {
1711
+ myResult = { ...myResult, ...result };
1712
+ });
1713
+
1714
+ // return the result
1715
+ const newResponse = {
1716
+ response: JSON.stringify(myResult, null, 2)
1717
+ };
1718
+ return callback(newResponse, null);
1719
+ });
1720
+ });
1721
+ } catch (ex) {
1722
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
1723
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1724
+ return callback(null, errorObj);
1725
+ }
1726
+ }
1727
+
1728
+ /**
1729
+ * @summary Gets the device count from the system
1730
+ *
1731
+ * @function iapGetDeviceCount
1732
+ *
1733
+ * @param {getCallback} callback - callback function to return the result
1734
+ * (count) or the error
1735
+ */
1736
+ iapGetDeviceCount(callback) {
1737
+ const meth = 'adapterBase-iapGetDeviceCount';
1738
+ const origin = `${this.id}-${meth}`;
1739
+ log.trace(origin);
1740
+
1741
+ // make sure we are set up for device broker getCount
1742
+ if (!this.allProps.devicebroker || !this.allProps.devicebroker.getCount || this.allProps.devicebroker.getCount.length === 0 || !this.allProps.devicebroker.getCount[0].path) {
1743
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Properties', ['devicebroker.getCount.path'], null, null, null);
1744
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1745
+ return callback(null, errorObj);
1746
+ }
1747
+
1748
+ // verify the required fields have been provided
1749
+
1750
+ try {
1751
+ const callPromises = [];
1752
+ for (let i = 0; i < this.allProps.devicebroker.getCount.length; i += 1) {
1753
+ // Perform component calls here.
1754
+ callPromises.push(
1755
+ new Promise((resolve, reject) => {
1756
+ this.iapMakeBrokerCall('getCount', this.allProps.devicebroker.getCount[i], null, null, (callRet, callErr) => {
1757
+ // return an error
1758
+ if (callErr) {
1759
+ reject(callErr);
1760
+ } else {
1761
+ // return the data
1762
+ resolve(callRet);
1763
+ }
1764
+ });
1765
+ })
1766
+ );
1767
+ }
1768
+
1769
+ // return an array of repsonses
1770
+ return Promise.all(callPromises).then((results) => {
1771
+ let myResult = {};
1772
+ results.forEach((result) => {
1773
+ myResult = { ...myResult, ...result };
1774
+ });
1775
+
1776
+ // return the result
1777
+ return callback({ count: myResult.length });
1778
+ });
1779
+ } catch (ex) {
1780
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
1781
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1782
+ return callback(null, errorObj);
1783
+ }
1784
+ }
1785
+ }
1786
+
1787
+ module.exports = AdapterBase;