@itentialopensource/adapter-utils 5.1.7 → 5.2.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.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,14 @@
1
1
 
2
+ ## 5.2.0 [10-04-2023]
3
+
4
+ * Broker Changes
5
+
6
+ Closes ADAPT-2848
7
+
8
+ See merge request itentialopensource/adapter-utils!277
9
+
10
+ ---
11
+
2
12
  ## 5.1.7 [09-09-2023]
3
13
 
4
14
  * Added pagination to expanded generic handler
@@ -7,7 +7,6 @@
7
7
 
8
8
  const AsyncLockCl = require('async-lock');
9
9
  const path = require('path');
10
- const jsonQuery = require('json-query');
11
10
 
12
11
  const lock = new AsyncLockCl();
13
12
  let id = null;
@@ -32,16 +31,6 @@ function createCacheEntity(entityName, entityList, interval, sortEntities = true
32
31
  });
33
32
  }
34
33
 
35
- /**
36
- * @summary Generates name for data object. Currently non-unique
37
- * @function generateName
38
- * @returns {String} - generated name
39
- */
40
- function generateName() {
41
- log.warn('Name for entity not found, generated name placeholder');
42
- return 'GeneratedName';
43
- }
44
-
45
34
  /**
46
35
  * @summary Deletes cache data and properties. Data deletion
47
36
  * saves considerable memory.
@@ -358,98 +347,6 @@ function removeCacheEntry(cache, entityType) {
358
347
  log.error(`${origin}: Did not find cache type ${entityType} to remove!`);
359
348
  }
360
349
 
361
- /**
362
- * @function getDataFromSources
363
- * @summary INTERNAL FUNCTION: get data from source(s) - nested
364
- * @param {*} loopField - fields
365
- * @param {Array} sources - sources to look into
366
- * @returns {*} - nested field value
367
- */
368
- function getDataFromSources(loopField, sources) {
369
- let fieldValue = loopField;
370
-
371
- // go through the sources to find the field
372
- for (let s = 0; s < sources.length; s += 1) {
373
- // find the field value using jsonquery
374
- const nestedValue = jsonQuery(loopField, { data: sources[s] }).value;
375
-
376
- // if we found in source - set and no need to check other sources
377
- if (nestedValue) {
378
- fieldValue = nestedValue;
379
- break;
380
- }
381
- }
382
-
383
- return fieldValue;
384
- }
385
-
386
- /**
387
- * @summary Manipulates data from call to only represent the specified fields and id and name if they do not have one
388
- *
389
- * @function parseResponseFields
390
- * @param {Boolean} responseFull - whether or not to take in all fields
391
- * @param {Object of output to call result} responseFields - fields to take in
392
- * - ie response field of "name": "id" will give the iap call output a name field
393
- * with the value being the id
394
- * @param {Array of Objects} allData - data returned from IAP Call
395
- * @return {Array of Objects} - modified data
396
- */
397
- function parseResponseFields(responseFields, allData, end, requestFields) {
398
- const origin = `${id}-cacheHandler-createCacheData`;
399
- log.trace(origin);
400
-
401
- const parsedData = [];
402
- const rfKeys = Object.keys(responseFields);
403
- log.debug(rfKeys);
404
-
405
- let ostypePrefix = '';
406
- if (responseFields.ostypePrefix) {
407
- ostypePrefix = responseFields.ostypePrefix;
408
- }
409
-
410
- let statusValue = true;
411
- if (responseFields.statusValue) {
412
- statusValue = responseFields.statusValue;
413
- }
414
-
415
- allData.response.forEach((currData) => {
416
- const newObj = currData;
417
-
418
- for (let rf = 0; rf < rfKeys.length; rf += 1) {
419
- if (rfKeys[rf] !== 'ostypePrefix') {
420
- let fieldValue = getDataFromSources(responseFields[rfKeys[rf]], [currData, { fake: 'fakedata' }, requestFields]);
421
-
422
- // if the field is ostype - need to add prefix
423
- if (rfKeys[rf] === 'ostype' && typeof fieldValue === 'string') {
424
- fieldValue = ostypePrefix + fieldValue;
425
- }
426
- // if there is a status to set, set it
427
- if (rfKeys[rf] === 'status') {
428
- // if really looking for just a good response
429
- if (responseFields[rfKeys[rf]] === 'return2xx' && allData.icode === statusValue.toString()) {
430
- newObj.isAlive = true;
431
- } else if (fieldValue.toString() === statusValue.toString()) {
432
- newObj.isAlive = true;
433
- } else {
434
- newObj.isAlive = false;
435
- }
436
- }
437
- // if we found a good value
438
- newObj[rfKeys[rf]] = fieldValue;
439
- }
440
- }
441
-
442
- if (!Object.prototype.hasOwnProperty.call(currData, 'name')) {
443
- newObj.name = generateName();
444
- }
445
-
446
- // no longer requiring id field
447
-
448
- parsedData.push(newObj);
449
- });
450
- return parsedData;
451
- }
452
-
453
350
  /**
454
351
  * @summary makes IAP Calls through Generic Handler
455
352
  *
@@ -458,9 +355,9 @@ function parseResponseFields(responseFields, allData, end, requestFields) {
458
355
  * @param requestHandler - instance of requestHandler to make call
459
356
  * @param callback - data or error
460
357
  */
461
- function makeIAPCall(calls, requestHandler, callback) { // todo pass in properties from cacheHandler
358
+ function makeIAPCall(calls, requestHandler, callback) {
462
359
  const callPromises = [];
463
- log.debug('Starting an iap call from cache.');
360
+
464
361
  for (let i = 0; i < calls.length; i += 1) {
465
362
  log.debug('Response :', calls[i].responseFields);
466
363
  callPromises.push(new Promise((resolve, reject) => {
@@ -469,7 +366,7 @@ function makeIAPCall(calls, requestHandler, callback) { // todo pass in properti
469
366
  metadata.pagination = calls[i].pagination;
470
367
  metadata.pagination.responseDatakey = calls[i].responseDatakey || '';
471
368
  }
472
- requestHandler.expandedGenericAdapterRequest(metadata, calls[i].path, calls[i].method, null, calls[i].query, calls[i].body, calls[i].headers, (callRet, callErr) => {
369
+ requestHandler.iapMakeGenericCall(metadata, calls[i].path, calls[i], [{ fake: 'fakedata' }], [], (callRet, callErr) => {
473
370
  if (callErr) {
474
371
  log.error('Make iap call failed with error');
475
372
  log.error(callErr);
@@ -477,21 +374,13 @@ function makeIAPCall(calls, requestHandler, callback) { // todo pass in properti
477
374
  log.error(callErr.IAPerror.displayString);
478
375
  return reject(callErr);
479
376
  }
480
- if (calls[i].handleFailure === 'ignore') {
481
- log.info(`Call failed for path ${calls[i].path} with error code ${callErr.icode}. Ignoring.`);
482
- return resolve([]);
483
- }
484
377
  log.warn(`Call failed for path ${calls[i].path} with error code ${callErr.icode}. Rejecting.`);
485
378
  return reject(callErr);
486
379
  }
487
380
 
488
381
  // callRet is the object returned from that call
489
382
  log.info(`Sucessful Call. Adding to cache from path ${calls[i].path}`);
490
- const result = callRet;
491
- if (calls[i].responseDatakey) {
492
- result.response = jsonQuery(calls[i].responseDatakey, { data: result.response }).value;
493
- }
494
- return resolve(parseResponseFields(calls[i].responseFields, result, i, calls[i].requestFields));
383
+ return resolve(callRet);
495
384
  });
496
385
  }));
497
386
  }
@@ -329,24 +329,109 @@ function dbCalls(collectionName, entity, action, data, callback) {
329
329
  /*
330
330
  * INTERNAL FUNCTION: get data from source(s) - nested
331
331
  */
332
- function getDataFromSources(loopField, sources) {
332
+ function getDataFromSources(loopField, sources, props) {
333
333
  let fieldValue = loopField;
334
-
334
+ let foundProp = false;
335
335
  // go through the sources to find the field
336
336
  for (let s = 0; s < sources.length; s += 1) {
337
337
  // find the field value using jsonquery
338
338
  const nestedValue = jsonQuery(loopField, { data: sources[s] }).value;
339
339
 
340
340
  // if we found in source - set and no need to check other sources
341
- if (nestedValue) {
341
+ if (nestedValue && Object.keys(nestedValue).length !== 0) {
342
342
  fieldValue = nestedValue;
343
+ foundProp = true;
343
344
  break;
344
345
  }
345
346
  }
346
347
 
348
+ // Check for field in adapter properties
349
+ if (!foundProp && props && props[loopField]) {
350
+ fieldValue = props[loopField];
351
+ }
352
+
347
353
  return fieldValue;
348
354
  }
349
355
 
356
+ /**
357
+ * @summary Extracts the keys to be replaced in request fields or response fields.
358
+ *
359
+ * @function extractKeysFromBraces
360
+ * @param {String} value - String in which to look for {} and extract values
361
+ *
362
+ * @return {Array} Array containing 0 or more keys found in input String
363
+ */
364
+ function extractKeysFromBraces(value) {
365
+ const regex = /\{(.*?)\}/g;
366
+ const matches = value.match(regex);
367
+ if (matches) {
368
+ return matches.map((key) => key.replace(/{|}/g, ''));
369
+ }
370
+ return [];
371
+ }
372
+
373
+ /**
374
+ * @summary Given a string containing a logical OR in braces {||}, return the LHS if value is found
375
+ * in sources, otherwise return the RHS
376
+ *
377
+ * @function setConditionalValue
378
+ * @param {String} valueField - String containing operands
379
+ * @param {Array} source - Array containing sources to search for operand values
380
+ * @param {Object} props - Object containing Adapter props as secondary source
381
+ *
382
+ * @return {String} Final value with data replaced in found keys.
383
+ */
384
+ function setConditionalValue(valueField, sources, props) {
385
+ if (!valueField.includes('{||}')) {
386
+ throw new Error('This method is only to be used with Strings containing a logical OR in braces {||}');
387
+ }
388
+ const operands = valueField.split('{||}'); // Array of left side and right side
389
+ const operandA = extractKeysFromBraces(operands[0]); // [name]
390
+ const operandB = extractKeysFromBraces(operands[1]); // [serial]
391
+ let finalValue = operands[0]; // {name}
392
+ for (let i = 0; i < operandA.length; i += 1) {
393
+ const fieldValue = getDataFromSources(operandA[i], sources, props);
394
+ if (fieldValue === operandA[i]) {
395
+ // did not find value - break here to try second part of conditional
396
+ [, finalValue] = operands;
397
+ break;
398
+ }
399
+ finalValue = finalValue.replace(`{${operandA[i]}}`, fieldValue);
400
+ }
401
+
402
+ // Check if broke out of loop due to missing value
403
+ if (finalValue !== operands[1]) {
404
+ return finalValue;
405
+ }
406
+
407
+ // Look for values for keys to the right of {||}
408
+ for (let j = 0; j < operandB.length; j += 1) {
409
+ const fieldValue = getDataFromSources(operandB[j], sources, props);
410
+ if (fieldValue === operandB[j]) {
411
+ throw new Error(`Could not find value in sources for ${operandA} or ${operandB}`);
412
+ }
413
+ finalValue = finalValue.replace(`{${operandB[j]}}`, fieldValue);
414
+ }
415
+
416
+ return finalValue;
417
+ }
418
+
419
+ function setResponseDataFromSources(loopField, sources, props) {
420
+ if (loopField.includes('{||}')) {
421
+ return setConditionalValue(loopField, sources, props);
422
+ }
423
+ let myField = loopField;
424
+ const keys = extractKeysFromBraces(loopField);
425
+ // Handle if val not found ?
426
+ for (let k = 0; k < keys.length; k += 1) {
427
+ const responseKey = keys[k];
428
+ const fieldValue = getDataFromSources(responseKey, sources, props);
429
+ myField = myField.replace(`{${responseKey}}`, fieldValue);
430
+ }
431
+
432
+ return myField;
433
+ }
434
+
350
435
  class RequestHandler {
351
436
  /**
352
437
  * Request Handler
@@ -553,19 +638,16 @@ class RequestHandler {
553
638
  let handleFail = 'fail';
554
639
  let ostypePrefix = '';
555
640
  let statusValue = 'true';
641
+ const sources = devResp.concat([callProps.requestFields]);
556
642
  if (callProps.path) {
557
643
  uriPath = `${callProps.path}`;
558
-
559
644
  // make any necessary changes to the path
560
- if (devResp !== null && callProps.requestFields && Object.keys(callProps.requestFields).length > 0) {
561
- const rqKeys = Object.keys(callProps.requestFields);
562
-
645
+ if (devResp !== null) {
563
646
  // get the field from the provided device
564
- for (let rq = 0; rq < rqKeys.length; rq += 1) {
565
- const fieldValue = getDataFromSources(callProps.requestFields[rqKeys[rq]], devResp);
566
-
567
- // put the value into the path - if it has been specified in the path
568
- uriPath = uriPath.replace(`{${rqKeys[rq]}}`, fieldValue);
647
+ const pathKeys = extractKeysFromBraces(uriPath);
648
+ for (let pathKey = 0; pathKey < pathKeys.length; pathKey += 1) {
649
+ const fieldValue = getDataFromSources(pathKeys[pathKey], sources, this.props);
650
+ uriPath = uriPath.replace(`{${pathKeys[pathKey]}}`, fieldValue);
569
651
  }
570
652
  }
571
653
  }
@@ -577,69 +659,41 @@ class RequestHandler {
577
659
  // go through the query params to check for variable values
578
660
  const cpKeys = Object.keys(callQuery);
579
661
  for (let cp = 0; cp < cpKeys.length; cp += 1) {
580
- // if (callQuery[cpKeys[cp]].startsWith('{') && callQuery[cpKeys[cp]].endsWith('}')) {
581
- // make any necessary changes to the query params
582
- if (devResp !== null && callProps.requestFields && Object.keys(callProps.requestFields).length > 0) {
583
- const rqKeys = Object.keys(callProps.requestFields);
584
-
585
- // get the field from the provided device
586
- for (let rq = 0; rq < rqKeys.length; rq += 1) {
587
- if (callQuery[cpKeys[cp]] === rqKeys[rq]) {
588
- const fieldValue = getDataFromSources(callProps.requestFields[rqKeys[rq]], devResp);
589
- // put the value into the query - if it has been specified in the query
590
- callQuery[cpKeys[cp]] = fieldValue;
591
- }
592
- }
662
+ // get array of values to replace
663
+ const matches = extractKeysFromBraces(callQuery[cpKeys[cp]]);
664
+ for (let m = 0; m < matches.length; m += 1) {
665
+ const queryKey = matches[m];
666
+ const fieldValue = getDataFromSources(queryKey, sources, this.props);
667
+ callQuery[cpKeys[cp]] = callQuery[cpKeys[cp]].replace(`{${queryKey}}`, fieldValue);
593
668
  }
594
- // }
595
669
  }
596
670
  }
597
671
  if (callProps.body) {
598
672
  callBody = { ...callProps.body };
599
-
600
673
  // go through the body fields to check for variable values
601
674
  const cbKeys = Object.keys(callBody);
602
675
  for (let cb = 0; cb < cbKeys.length; cb += 1) {
603
- // if (callBody[cbKeys[cb]].startsWith('{') && callBody[cbKeys[cb]].endsWith('}')) {
676
+ const matches = extractKeysFromBraces(callBody[cbKeys[cb]]);
677
+ for (let m = 0; m < matches.length; m += 1) {
604
678
  // make any necessary changes to the query params
605
- if (devResp !== null && callProps.requestFields && Object.keys(callProps.requestFields).length > 0) {
606
- const rqKeys = Object.keys(callProps.requestFields);
607
-
608
- // get the field from the provided device
609
- for (let rq = 0; rq < rqKeys.length; rq += 1) {
610
- if (callBody[cbKeys[cb]] === rqKeys[rq]) {
611
- const fieldValue = getDataFromSources(callProps.requestFields[rqKeys[rq]], devResp);
612
-
613
- // put the value into the query - if it has been specified in the query
614
- callBody[cbKeys[cb]] = fieldValue;
615
- }
616
- }
679
+ const bodyKey = matches[m];
680
+ const fieldValue = getDataFromSources(bodyKey, sources, this.props);
681
+ callBody[cbKeys[cb]] = callBody[cbKeys[cb]].replace(`{${bodyKey}}`, fieldValue);
617
682
  }
618
- // }
619
683
  }
620
684
  }
621
685
  if (callProps.headers) {
622
686
  callHeaders = { ...callProps.headers };
623
-
624
- // go through the body fields to check for variable values
687
+ // go through the header fields to check for variable values
625
688
  const chKeys = Object.keys(callHeaders);
626
689
  for (let ch = 0; ch < chKeys.length; ch += 1) {
627
- // if (callHeaders[chKeys[ch]].startsWith('{') && callHeaders[chKeys[ch]].endsWith('}')) {
628
- // make any necessary changes to the query params
629
- if (devResp !== null && callProps.requestFields && Object.keys(callProps.requestFields).length > 0) {
630
- const rqKeys = Object.keys(callProps.requestFields);
631
-
632
- // get the field from the provided device
633
- for (let rq = 0; rq < rqKeys.length; rq += 1) {
634
- if (callHeaders[chKeys[ch]] === rqKeys[rq]) {
635
- const fieldValue = getDataFromSources(callProps.requestFields[rqKeys[rq]], devResp);
636
-
637
- // put the value into the query - if it has been specified in the query
638
- callHeaders[chKeys[ch]] = fieldValue;
639
- }
640
- }
690
+ const matches = extractKeysFromBraces(callHeaders[chKeys[ch]]);
691
+ for (let m = 0; m < matches.length; m += 1) {
692
+ const headerKey = matches[m];
693
+ // make any necessary changes to the query params
694
+ const fieldValue = getDataFromSources(headerKey, sources, this.props);
695
+ callHeaders[chKeys[ch]] = callQuery[chKeys[ch]].replace(`{${headerKey}}`, fieldValue);
641
696
  }
642
- // }
643
697
  }
644
698
  }
645
699
  if (callProps.handleFailure) {
@@ -661,6 +715,9 @@ class RequestHandler {
661
715
  return this.expandedGenericAdapterRequest(metadata, uriPath, uriMethod, null, callQuery, callBody, callHeaders, (result, error) => {
662
716
  // if we received an error or their is no response on the results return an error
663
717
  if (error) {
718
+ if (this.props.stub && error.icode === 'AD.301') {
719
+ return callback(null, error);
720
+ }
664
721
  if (handleFail === 'fail') {
665
722
  return callback(null, error);
666
723
  }
@@ -694,7 +751,8 @@ class RequestHandler {
694
751
  const thisDevice = myResult.response[a];
695
752
  for (let rf = 0; rf < rfKeys.length; rf += 1) {
696
753
  if (rfKeys[rf] !== 'ostypePrefix') {
697
- let fieldValue = getDataFromSources(callProps.responseFields[rfKeys[rf]], [thisDevice, devResp, callProps.requestFields]);
754
+ // devResp removed due to conditional issues
755
+ let fieldValue = setResponseDataFromSources(callProps.responseFields[rfKeys[rf]], [thisDevice, callProps.requestFields]);
698
756
 
699
757
  // if the field is ostype - need to add prefix
700
758
  if (rfKeys[rf] === 'ostype' && typeof fieldValue === 'string') {
@@ -744,8 +802,7 @@ class RequestHandler {
744
802
  for (let rf = 0; rf < rfKeys.length; rf += 1) {
745
803
  // skip ostypePrefix since it is not a field
746
804
  if (rfKeys[rf] !== 'ostypePrefix') {
747
- let fieldValue = getDataFromSources(callProps.responseFields[rfKeys[rf]], [thisDevice, devResp, callProps.requestFields]);
748
-
805
+ let fieldValue = setResponseDataFromSources(callProps.responseFields[rfKeys[rf]], [thisDevice, devResp, callProps.requestFields]);
749
806
  // if the field is ostype - need to add prefix
750
807
  if (rfKeys[rf] === 'ostype' && typeof fieldValue === 'string') {
751
808
  fieldValue = ostypePrefix + fieldValue;
@@ -785,6 +842,7 @@ class RequestHandler {
785
842
  });
786
843
  } catch (e) {
787
844
  const errorObj = this.formatErrorObject(this.myid, meth, 'Caught Exception', null, null, null, e);
845
+ log.debug(`actual error: ${JSON.stringify(e)}`);
788
846
  log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
789
847
  return callback(null, errorObj);
790
848
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itentialopensource/adapter-utils",
3
- "version": "5.1.7",
3
+ "version": "5.2.0",
4
4
  "description": "Itential Adapter Utility Libraries",
5
5
  "scripts": {
6
6
  "postinstall": "node utils/setup.js",
Binary file