@itentialopensource/adapter-gcp_compute 1.0.1 → 1.1.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,12 @@
1
1
 
2
+ ## 1.1.0 [01-21-2022]
3
+
4
+ * migrate to the latest foundation and broker ready
5
+
6
+ See merge request itentialopensource/adapters/cloud/adapter-gcp_compute!5
7
+
8
+ ---
9
+
2
10
  ## 1.0.1 [12-21-2021]
3
11
 
4
12
  * Don't call readPropsKeyFile in the constructor - if already have a key
package/adapter.js CHANGED
@@ -118,6 +118,8 @@ class GcpCompute extends AdapterBaseCl {
118
118
  */
119
119
  getWorkflowFunctions(inIgnore) {
120
120
  let myIgnore = [
121
+ 'hasEntities',
122
+ 'hasDevices',
121
123
  'configureGtoken',
122
124
  'getNested',
123
125
  'readPropsKeyFile',
@@ -294,6 +296,24 @@ class GcpCompute extends AdapterBaseCl {
294
296
  }
295
297
  }
296
298
 
299
+ /**
300
+ * @summary moves entites into Mongo DB
301
+ *
302
+ * @function moveEntitiesToDB
303
+ * @param {getCallback} callback - a callback function to return the result (Generics)
304
+ * or the error
305
+ */
306
+ moveEntitiesToDB(callback) {
307
+ const origin = `${this.id}-adapter-moveEntitiesToDB`;
308
+ log.trace(origin);
309
+ try {
310
+ return super.moveEntitiesToDB(callback);
311
+ } catch (err) {
312
+ log.error(`${origin}: ${err}`);
313
+ return callback(null, err);
314
+ }
315
+ }
316
+
297
317
  /**
298
318
  * @summary Determines if this adapter supports the specific entity
299
319
  *
@@ -573,6 +593,456 @@ class GcpCompute extends AdapterBaseCl {
573
593
  }
574
594
  }
575
595
 
596
+ /* BROKER CALLS */
597
+ /**
598
+ * @summary Determines if this adapter supports any in a list of entities
599
+ *
600
+ * @function hasEntities
601
+ * @param {String} entityType - the entity type to check for
602
+ * @param {Array} entityList - the list of entities we are looking for
603
+ *
604
+ * @param {Callback} callback - A map where the entity is the key and the
605
+ * value is true or false
606
+ */
607
+ hasEntities(entityType, entityList, callback) {
608
+ const origin = `${this.id}-adapter-hasEntities`;
609
+ log.trace(origin);
610
+
611
+ switch (entityType) {
612
+ case 'Device':
613
+ return this.hasDevices(entityList, callback);
614
+ default:
615
+ return callback(null, `${this.id} does not support entity ${entityType}`);
616
+ }
617
+ }
618
+
619
+ /**
620
+ * @summary Helper method for hasEntities for the specific device case
621
+ *
622
+ * @param {Array} deviceList - array of unique device identifiers
623
+ * @param {Callback} callback - A map where the device is the key and the
624
+ * value is true or false
625
+ */
626
+ hasDevices(deviceList, callback) {
627
+ const origin = `${this.id}-adapter-hasDevices`;
628
+ log.trace(origin);
629
+
630
+ const findings = deviceList.reduce((map, device) => {
631
+ // eslint-disable-next-line no-param-reassign
632
+ map[device] = false;
633
+ log.debug(`In reduce: ${JSON.stringify(map)}`);
634
+ return map;
635
+ }, {});
636
+ const apiCalls = deviceList.map((device) => new Promise((resolve) => {
637
+ this.getDevice(device, (result, error) => {
638
+ if (error) {
639
+ log.debug(`In map error: ${JSON.stringify(device)}`);
640
+ return resolve({ name: device, found: false });
641
+ }
642
+ log.debug(`In map: ${JSON.stringify(device)}`);
643
+ return resolve({ name: device, found: true });
644
+ });
645
+ }));
646
+ Promise.all(apiCalls).then((results) => {
647
+ results.forEach((device) => {
648
+ findings[device.name] = device.found;
649
+ });
650
+ log.debug(`FINDINGS: ${JSON.stringify(findings)}`);
651
+ return callback(findings);
652
+ }).catch((errors) => {
653
+ log.error('Unable to do device lookup.');
654
+ return callback(null, { code: 503, message: 'Unable to do device lookup.', error: errors });
655
+ });
656
+ }
657
+
658
+ /**
659
+ * @summary Get Appliance that match the deviceName
660
+ *
661
+ * @function getDevice
662
+ * @param {String} deviceName - the deviceName to find (required)
663
+ *
664
+ * @param {getCallback} callback - a callback function to return the result
665
+ * (appliance) or the error
666
+ */
667
+ getDevice(deviceName, callback) {
668
+ const meth = 'adapter-getDevice';
669
+ const origin = `${this.id}-${meth}`;
670
+ log.trace(origin);
671
+
672
+ if (this.suspended && this.suspendMode === 'error') {
673
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'AD.600', [], null, null, null);
674
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
675
+ return callback(null, errorObj);
676
+ }
677
+
678
+ /* HERE IS WHERE YOU VALIDATE DATA */
679
+ if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
680
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['deviceName'], null, null, null);
681
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
682
+ return callback(null, errorObj);
683
+ }
684
+
685
+ try {
686
+ // need to get the device so we can convert the deviceName to an id
687
+ // !! if we can do a lookup by name the getDevicesFiltered may not be necessary
688
+ const opts = {
689
+ filter: {
690
+ name: deviceName
691
+ }
692
+ };
693
+ return this.getDevicesFiltered(opts, (devs, ferr) => {
694
+ // if we received an error or their is no response on the results return an error
695
+ if (ferr) {
696
+ return callback(null, ferr);
697
+ }
698
+ if (devs.list.length < 1) {
699
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
700
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
701
+ return callback(null, errorObj);
702
+ }
703
+ // get the uuid from the device
704
+ const { uuid } = devs.list[0];
705
+
706
+ // !! using Generic makes it easier on the Adapter Builder (just need to change the path)
707
+ // !! you can also replace with a specific call if that is easier
708
+ const uriPath = `/call/toget/device/${uuid}`;
709
+ return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
710
+ // if we received an error or their is no response on the results return an error
711
+ if (error) {
712
+ return callback(null, error);
713
+ }
714
+ if (!result.response || !result.response.applianceMo) {
715
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Invalid Response', ['getDevice'], null, null, null);
716
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
717
+ return callback(null, errorObj);
718
+ }
719
+
720
+ // return the response
721
+ // !! format the data we send back
722
+ // !! these fields are config manager fields you need to map to the data we receive
723
+ const thisDevice = result.response;
724
+ thisDevice.name = thisDevice.systemName;
725
+ thisDevice.ostype = `System-${thisDevice.systemType}`;
726
+ thisDevice.port = thisDevice.systemPort;
727
+ thisDevice.ipaddress = thisDevice.systemIP;
728
+ return callback(thisDevice);
729
+ });
730
+ });
731
+ } catch (ex) {
732
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
733
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
734
+ return callback(null, errorObj);
735
+ }
736
+ }
737
+
738
+ /**
739
+ * @summary Get Appliances that match the filter
740
+ *
741
+ * @function getDevicesFiltered
742
+ * @param {Object} options - the data to use to filter the appliances (optional)
743
+ *
744
+ * @param {getCallback} callback - a callback function to return the result
745
+ * (appliances) or the error
746
+ */
747
+ getDevicesFiltered(options, callback) {
748
+ const meth = 'adapter-getDevicesFiltered';
749
+ const origin = `${this.id}-${meth}`;
750
+ log.trace(origin);
751
+
752
+ // verify the required fields have been provided
753
+ if (options === undefined || options === null || options === '' || options.length === 0) {
754
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['options'], null, null, null);
755
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
756
+ return callback(null, errorObj);
757
+ }
758
+ log.debug(`Device Filter Options: ${JSON.stringify(options)}`);
759
+
760
+ // TODO - get pagination working
761
+ // const nextToken = options.start;
762
+ // const maxResults = options.limit;
763
+
764
+ // set up the filter of Device Names
765
+ let filterName = [];
766
+ if (options && options.filter && options.filter.name) {
767
+ // when this hack is removed, remove the lint ignore above
768
+ if (Array.isArray(options.filter.name)) {
769
+ // eslint-disable-next-line prefer-destructuring
770
+ filterName = options.filter.name;
771
+ } else {
772
+ filterName = [options.filter.name];
773
+ }
774
+ }
775
+
776
+ // TODO - get sort and order working
777
+ /*
778
+ if (options && options.sort) {
779
+ reqObj.uriOptions.sort = JSON.stringify(options.sort);
780
+ }
781
+ if (options && options.order) {
782
+ reqObj.uriOptions.order = options.order;
783
+ }
784
+ */
785
+ try {
786
+ // !! using Generic makes it easier on the Adapter Builder (just need to change the path)
787
+ // !! you can also replace with a specific call if that is easier
788
+ const uriPath = '/call/toget/devices';
789
+ return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
790
+ // if we received an error or their is no response on the results return an error
791
+ if (error) {
792
+ return callback(null, error);
793
+ }
794
+ if (!result.response) {
795
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Invalid Response', ['getDevicesFiltered'], null, null, null);
796
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
797
+ return callback(null, errorObj);
798
+ }
799
+
800
+ // !! go through the response - may have to look for sub object
801
+ // handle an array of devices
802
+ if (Array.isArray(result.response)) {
803
+ const myDevices = [];
804
+
805
+ for (let d = 0; d < result.response.length; d += 1) {
806
+ // !! format the data we send back
807
+ // !! these fields are config manager fields you need to map to the data we receive
808
+ const thisDevice = result.response;
809
+ thisDevice.name = thisDevice.systemName;
810
+ thisDevice.ostype = `System-${thisDevice.systemType}`;
811
+ thisDevice.port = thisDevice.systemPort;
812
+ thisDevice.ipaddress = thisDevice.systemIP;
813
+
814
+ // if there is no filter - return the device
815
+ if (filterName.length === 0) {
816
+ myDevices.push(thisDevice);
817
+ } else {
818
+ // if we have to match a filter
819
+ let found = false;
820
+ for (let f = 0; f < filterName.length; f += 1) {
821
+ if (thisDevice.name.indexOf(filterName[f]) >= 0) {
822
+ found = true;
823
+ break;
824
+ }
825
+ }
826
+ // matching device
827
+ if (found) {
828
+ myDevices.push(thisDevice);
829
+ }
830
+ }
831
+ }
832
+ log.debug(`${origin}: Found #${myDevices.length} devices.`);
833
+ log.debug(`Devices: ${JSON.stringify(myDevices)}`);
834
+ return callback({ total: myDevices.length, list: myDevices });
835
+ }
836
+ // handle a single device response
837
+ // !! format the data we send back
838
+ // !! these fields are config manager fields you need to map to the data we receive
839
+ const thisDevice = result.response;
840
+ thisDevice.name = thisDevice.systemName;
841
+ thisDevice.ostype = `System-${thisDevice.systemType}`;
842
+ thisDevice.port = thisDevice.systemPort;
843
+ thisDevice.ipaddress = thisDevice.systemIP;
844
+
845
+ // if there is no filter - return the device
846
+ if (filterName.length === 0) {
847
+ log.debug(`${origin}: Found #1 device.`);
848
+ log.debug(`Device: ${JSON.stringify(thisDevice)}`);
849
+ return callback({ total: 1, list: [thisDevice] });
850
+ }
851
+
852
+ // if there is a filter need to check for matching device
853
+ let found = false;
854
+ for (let f = 0; f < filterName.length; f += 1) {
855
+ if (thisDevice.name.indexOf(filterName[f]) >= 0) {
856
+ found = true;
857
+ break;
858
+ }
859
+ }
860
+ // matching device
861
+ if (found) {
862
+ log.debug(`${origin}: Found #1 device.`);
863
+ log.debug(`Device Found: ${JSON.stringify(thisDevice)}`);
864
+ return callback({ total: 1, list: [thisDevice] });
865
+ }
866
+ // not a matching device
867
+ log.debug(`${origin}: No matching device found.`);
868
+ return callback({ total: 0, list: [] });
869
+ });
870
+ } catch (ex) {
871
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
872
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
873
+ return callback(null, errorObj);
874
+ }
875
+ }
876
+
877
+ /**
878
+ * @summary Gets the status for the provided appliance
879
+ *
880
+ * @function isAlive
881
+ * @param {String} deviceName - the deviceName of the appliance. (required)
882
+ *
883
+ * @param {configCallback} callback - callback function to return the result
884
+ * (appliance isAlive) or the error
885
+ */
886
+ isAlive(deviceName, callback) {
887
+ const meth = 'adapter-isAlive';
888
+ const origin = `${this.id}-${meth}`;
889
+ log.trace(origin);
890
+
891
+ // verify the required fields have been provided
892
+ if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
893
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['deviceName'], null, null, null);
894
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
895
+ return callback(null, errorObj);
896
+ }
897
+
898
+ try {
899
+ // need to get the device so we can convert the deviceName to an id
900
+ // !! if we can do a lookup by name the getDevicesFiltered may not be necessary
901
+ const opts = {
902
+ filter: {
903
+ name: deviceName
904
+ }
905
+ };
906
+ return this.getDevicesFiltered(opts, (devs, ferr) => {
907
+ // if we received an error or their is no response on the results return an error
908
+ if (ferr) {
909
+ return callback(null, ferr);
910
+ }
911
+ if (devs.list.length < 1) {
912
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
913
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
914
+ return callback(null, errorObj);
915
+ }
916
+ // get the uuid from the device
917
+ const { uuid } = devs.list[0];
918
+
919
+ // !! using Generic makes it easier on the Adapter Builder (just need to change the path)
920
+ // !! you can also replace with a specific call if that is easier
921
+ const uriPath = `/call/toget/status/${uuid}`;
922
+ return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
923
+ // if we received an error or their is no response on the results return an error
924
+ if (error) {
925
+ return callback(null, error);
926
+ }
927
+ // !! should update this to make sure we are checking for the appropriate object/field
928
+ if (!result.response || !result.response.returnObj || !Object.hasOwnProperty.call(result.response.returnObj, 'statusField')) {
929
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Invalid Response', ['isAlive'], null, null, null);
930
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
931
+ return callback(null, errorObj);
932
+ }
933
+
934
+ // !! return the response - Update to the appropriate object/field
935
+ return callback(!result.response.returnObj.statusField);
936
+ });
937
+ });
938
+ } catch (ex) {
939
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
940
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
941
+ return callback(null, errorObj);
942
+ }
943
+ }
944
+
945
+ /**
946
+ * @summary Gets a config for the provided Appliance
947
+ *
948
+ * @function getConfig
949
+ * @param {String} deviceName - the deviceName of the appliance. (required)
950
+ * @param {String} format - the desired format of the config. (optional)
951
+ *
952
+ * @param {configCallback} callback - callback function to return the result
953
+ * (appliance config) or the error
954
+ */
955
+ getConfig(deviceName, format, callback) {
956
+ const meth = 'adapter-getConfig';
957
+ const origin = `${this.id}-${meth}`;
958
+ log.trace(origin);
959
+
960
+ // verify the required fields have been provided
961
+ if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
962
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['deviceName'], null, null, null);
963
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
964
+ return callback(null, errorObj);
965
+ }
966
+
967
+ try {
968
+ // need to get the device so we can convert the deviceName to an id
969
+ // !! if we can do a lookup by name the getDevicesFiltered may not be necessary
970
+ const opts = {
971
+ filter: {
972
+ name: deviceName
973
+ }
974
+ };
975
+ return this.getDevicesFiltered(opts, (devs, ferr) => {
976
+ // if we received an error or their is no response on the results return an error
977
+ if (ferr) {
978
+ return callback(null, ferr);
979
+ }
980
+ if (devs.list.length < 1) {
981
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
982
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
983
+ return callback(null, errorObj);
984
+ }
985
+ // get the uuid from the device
986
+ const { uuid } = devs.list[0];
987
+
988
+ // !! using Generic makes it easier on the Adapter Builder (just need to change the path)
989
+ // !! you can also replace with a specific call if that is easier
990
+ const uriPath = `/call/toget/config/${uuid}`;
991
+ return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
992
+ // if we received an error or their is no response on the results return an error
993
+ if (error) {
994
+ return callback(null, error);
995
+ }
996
+
997
+ // return the result
998
+ const newResponse = {
999
+ response: JSON.stringify(result.response, null, 2)
1000
+ };
1001
+ return callback(newResponse);
1002
+ });
1003
+ });
1004
+ } catch (ex) {
1005
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
1006
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1007
+ return callback(null, errorObj);
1008
+ }
1009
+ }
1010
+
1011
+ /**
1012
+ * @summary Gets the device count from the system
1013
+ *
1014
+ * @function getCount
1015
+ *
1016
+ * @param {getCallback} callback - callback function to return the result
1017
+ * (count) or the error
1018
+ */
1019
+ getCount(callback) {
1020
+ const meth = 'adapter-getCount';
1021
+ const origin = `${this.id}-${meth}`;
1022
+ log.trace(origin);
1023
+
1024
+ // verify the required fields have been provided
1025
+
1026
+ try {
1027
+ // !! using Generic makes it easier on the Adapter Builder (just need to change the path)
1028
+ // !! you can also replace with a specific call if that is easier
1029
+ const uriPath = '/call/toget/count';
1030
+ return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
1031
+ // if we received an error or their is no response on the results return an error
1032
+ if (error) {
1033
+ return callback(null, error);
1034
+ }
1035
+
1036
+ // return the result
1037
+ return callback({ count: result.response });
1038
+ });
1039
+ } catch (ex) {
1040
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
1041
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1042
+ return callback(null, errorObj);
1043
+ }
1044
+ }
1045
+
576
1046
  /**
577
1047
  * @callback healthCallback
578
1048
  * @param {Object} result - the result of the get request (contains an id and a status)
package/adapterBase.js CHANGED
@@ -822,7 +822,7 @@ class AdapterBase extends EventEmitterCl {
822
822
  */
823
823
  async runConnectivity(callback) {
824
824
  try {
825
- const { serviceItem } = await troubleshootingAdapter.getAdapterConfig();
825
+ const { serviceItem } = await tbUtils.getAdapterConfig();
826
826
  const { host } = serviceItem.properties.properties;
827
827
  const result = tbUtils.runConnectivity(host, false);
828
828
  if (result.failCount > 0) {
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@itentialopensource/adapter-gcp_compute",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "This adapter integrates with system described as: computeEngineApi.",
5
5
  "main": "adapter.js",
6
6
  "wizardVersion": "2.44.7",
7
- "engineVersion": "1.59.50",
7
+ "engineVersion": "1.59.55",
8
8
  "adapterType": "http",
9
9
  "scripts": {
10
10
  "artifactize": "npm i && node utils/packModificationScript.js",
package/pronghorn.json CHANGED
@@ -386,6 +386,198 @@
386
386
  },
387
387
  "task": true
388
388
  },
389
+ {
390
+ "name": "moveEntitiesToDB",
391
+ "summary": "Moves entities from an adapter into the IAP database",
392
+ "description": "Moves entities from an adapter into the IAP database",
393
+ "input": [],
394
+ "output": {
395
+ "name": "res",
396
+ "type": "object",
397
+ "description": "A JSON Object containing status, code and the response from the mongo transaction",
398
+ "schema": {
399
+ "title": "res",
400
+ "type": "object"
401
+ }
402
+ },
403
+ "roles": [
404
+ "admin"
405
+ ],
406
+ "route": {
407
+ "verb": "POST",
408
+ "path": "/moveEntitiesToDB"
409
+ },
410
+ "task": true
411
+ },
412
+ {
413
+ "name": "getDevice",
414
+ "summary": "Get the Appliance",
415
+ "description": "Get the Appliance",
416
+ "input": [
417
+ {
418
+ "name": "deviceName",
419
+ "type": "string",
420
+ "info": "An Appliance Device Name",
421
+ "required": true,
422
+ "schema": {
423
+ "title": "deviceName",
424
+ "type": "string"
425
+ }
426
+ }
427
+ ],
428
+ "output": {
429
+ "name": "result",
430
+ "type": "object",
431
+ "description": "A JSON Object containing status, code and the result",
432
+ "schema": {
433
+ "title": "result",
434
+ "type": "object"
435
+ }
436
+ },
437
+ "roles": [
438
+ "admin"
439
+ ],
440
+ "route": {
441
+ "verb": "POST",
442
+ "path": "/getDevice"
443
+ },
444
+ "task": false
445
+ },
446
+ {
447
+ "name": "getDevicesFiltered",
448
+ "summary": "Get Appliances that match the filter",
449
+ "description": "Get Appliances that match the filter",
450
+ "input": [
451
+ {
452
+ "name": "options",
453
+ "type": "object",
454
+ "info": "options - e.g. { 'start': 1, 'limit': 20, 'filter': { 'name': 'abc123' } }",
455
+ "required": true,
456
+ "schema": {
457
+ "title": "options",
458
+ "type": "object"
459
+ }
460
+ }
461
+ ],
462
+ "output": {
463
+ "name": "result",
464
+ "type": "array",
465
+ "description": "A JSON Object containing status, code and the result",
466
+ "schema": {
467
+ "title": "result",
468
+ "type": "array"
469
+ }
470
+ },
471
+ "roles": [
472
+ "admin"
473
+ ],
474
+ "route": {
475
+ "verb": "POST",
476
+ "path": "/getDevicesFiltered"
477
+ },
478
+ "task": false
479
+ },
480
+ {
481
+ "name": "isAlive",
482
+ "summary": "Checks the status for the provided Appliance",
483
+ "description": "Checks the status for the provided Appliance",
484
+ "input": [
485
+ {
486
+ "name": "deviceName",
487
+ "type": "string",
488
+ "info": "An Appliance Device Name",
489
+ "required": true,
490
+ "schema": {
491
+ "title": "deviceName",
492
+ "type": "string"
493
+ }
494
+ }
495
+ ],
496
+ "output": {
497
+ "name": "result",
498
+ "type": "boolean",
499
+ "description": "A JSON Object containing status, code and the result",
500
+ "schema": {
501
+ "title": "result",
502
+ "type": "boolean"
503
+ }
504
+ },
505
+ "roles": [
506
+ "admin"
507
+ ],
508
+ "route": {
509
+ "verb": "POST",
510
+ "path": "/isAlive"
511
+ },
512
+ "task": false
513
+ },
514
+ {
515
+ "name": "getConfig",
516
+ "summary": "Gets a config for the provided Appliance",
517
+ "description": "Gets a config for the provided Appliance",
518
+ "input": [
519
+ {
520
+ "name": "deviceName",
521
+ "type": "string",
522
+ "info": "An Appliance Device Name",
523
+ "required": true,
524
+ "schema": {
525
+ "title": "deviceName",
526
+ "type": "string"
527
+ }
528
+ },
529
+ {
530
+ "name": "format",
531
+ "type": "string",
532
+ "info": "The format to be returned - this is ignored as we always return json",
533
+ "required": false,
534
+ "schema": {
535
+ "title": "format",
536
+ "type": "string"
537
+ }
538
+ }
539
+ ],
540
+ "output": {
541
+ "name": "result",
542
+ "type": "object",
543
+ "description": "A JSON Object containing status, code and the result",
544
+ "schema": {
545
+ "title": "result",
546
+ "type": "object"
547
+ }
548
+ },
549
+ "roles": [
550
+ "admin"
551
+ ],
552
+ "route": {
553
+ "verb": "POST",
554
+ "path": "/getConfig"
555
+ },
556
+ "task": false
557
+ },
558
+ {
559
+ "name": "getCount",
560
+ "summary": "Gets a device count from the system",
561
+ "description": "Gets a device count from the system",
562
+ "input": [],
563
+ "output": {
564
+ "name": "result",
565
+ "type": "object",
566
+ "description": "A JSON Object containing status, code and the result",
567
+ "schema": {
568
+ "title": "result",
569
+ "type": "object"
570
+ }
571
+ },
572
+ "roles": [
573
+ "admin"
574
+ ],
575
+ "route": {
576
+ "verb": "POST",
577
+ "path": "/getCount"
578
+ },
579
+ "task": false
580
+ },
389
581
  {
390
582
  "name": "getProject",
391
583
  "summary": "Returns the specified Project resource.",
Binary file
@@ -0,0 +1,95 @@
1
+ {
2
+ "errors": [],
3
+ "statistics": [
4
+ {
5
+ "owner": "errorJson",
6
+ "description": "New adapter errors available for use",
7
+ "value": 0
8
+ },
9
+ {
10
+ "owner": "errorJson",
11
+ "description": "Adapter errors no longer available for use",
12
+ "value": 0
13
+ },
14
+ {
15
+ "owner": "errorJson",
16
+ "description": "Adapter errors that have been updated (e.g. recommendation changes)",
17
+ "value": 30
18
+ },
19
+ {
20
+ "owner": "packageJson",
21
+ "description": "Number of production dependencies",
22
+ "value": 13
23
+ },
24
+ {
25
+ "owner": "packageJson",
26
+ "description": "Number of development dependencies",
27
+ "value": 7
28
+ },
29
+ {
30
+ "owner": "packageJson",
31
+ "description": "Number of npm scripts",
32
+ "value": 23
33
+ },
34
+ {
35
+ "owner": "packageJson",
36
+ "description": "Runtime Library dependency",
37
+ "value": "^4.44.11"
38
+ },
39
+ {
40
+ "owner": "propertiesSchemaJson",
41
+ "description": "Adapter properties defined in the propertiesSchema file",
42
+ "value": 65
43
+ },
44
+ {
45
+ "owner": "readmeMd",
46
+ "description": "Number of lines in the README.md",
47
+ "value": 688
48
+ },
49
+ {
50
+ "owner": "unitTestJS",
51
+ "description": "Number of lines of code in unit tests",
52
+ "value": 29039
53
+ },
54
+ {
55
+ "owner": "unitTestJS",
56
+ "description": "Number of unit tests",
57
+ "value": 1852
58
+ },
59
+ {
60
+ "owner": "integrationTestJS",
61
+ "description": "Number of lines of code in integration tests",
62
+ "value": 31265
63
+ },
64
+ {
65
+ "owner": "integrationTestJS",
66
+ "description": "Number of integration tests",
67
+ "value": 563
68
+ },
69
+ {
70
+ "owner": "staticFile",
71
+ "description": "Number of lines of code in adapterBase.js",
72
+ "value": 1029
73
+ },
74
+ {
75
+ "owner": "staticFile",
76
+ "description": "Number of static files added",
77
+ "value": 34
78
+ },
79
+ {
80
+ "owner": "Overall",
81
+ "description": "Total lines of Code",
82
+ "value": 61333
83
+ },
84
+ {
85
+ "owner": "Overall",
86
+ "description": "Total Tests",
87
+ "value": 2415
88
+ },
89
+ {
90
+ "owner": "Overall",
91
+ "description": "Total Files",
92
+ "value": 6
93
+ }
94
+ ]
95
+ }
@@ -13,7 +13,10 @@ const winston = require('winston');
13
13
  const { expect } = require('chai');
14
14
  const { use } = require('chai');
15
15
  const td = require('testdouble');
16
+ const util = require('util');
17
+ const pronghorn = require('../../pronghorn.json');
16
18
 
19
+ pronghorn.methodsByName = pronghorn.methods.reduce((result, meth) => ({ ...result, [meth.name]: meth }), {});
17
20
  const anything = td.matchers.anything();
18
21
 
19
22
  // stub and attemptTimeout are used throughout the code so set them here
@@ -64,6 +67,9 @@ global.pronghornProps = {
64
67
  auth_field: 'header.headers.Authorization',
65
68
  auth_field_format: 'Basic {b64}{username}:{password}{/b64}',
66
69
  auth_logging: false,
70
+ client_id: '',
71
+ client_secret: '',
72
+ grant_type: '',
67
73
  configureGtoken: {
68
74
  email: '',
69
75
  scope: 'https://www.googleapis.com/auth/cloud-platform',
@@ -66,6 +66,9 @@ global.pronghornProps = {
66
66
  auth_field: 'header.headers.Authorization',
67
67
  auth_field_format: 'Basic {b64}{username}:{password}{/b64}',
68
68
  auth_logging: false,
69
+ client_id: '',
70
+ client_secret: '',
71
+ grant_type: '',
69
72
  configureGtoken: {
70
73
  email: '',
71
74
  scope: 'https://www.googleapis.com/auth/cloud-platform',
@@ -1138,6 +1141,18 @@ describe('[unit] GcpCompute Adapter Test', () => {
1138
1141
  });
1139
1142
  });
1140
1143
 
1144
+ describe('#moveEntitiesToDB', () => {
1145
+ it('should have a moveEntitiesToDB function', (done) => {
1146
+ try {
1147
+ assert.equal(true, typeof a.moveEntitiesToDB === 'function');
1148
+ done();
1149
+ } catch (error) {
1150
+ log.error(`Test Failure: ${error}`);
1151
+ done(error);
1152
+ }
1153
+ });
1154
+ });
1155
+
1141
1156
  describe('#checkActionFiles', () => {
1142
1157
  it('should have a checkActionFiles function', (done) => {
1143
1158
  try {
@@ -1267,6 +1282,90 @@ describe('[unit] GcpCompute Adapter Test', () => {
1267
1282
  // }).timeout(attemptTimeout);
1268
1283
  // });
1269
1284
 
1285
+ describe('#hasEntities', () => {
1286
+ it('should have a hasEntities function', (done) => {
1287
+ try {
1288
+ assert.equal(true, typeof a.hasEntities === 'function');
1289
+ done();
1290
+ } catch (error) {
1291
+ log.error(`Test Failure: ${error}`);
1292
+ done(error);
1293
+ }
1294
+ });
1295
+ });
1296
+
1297
+ describe('#hasDevices', () => {
1298
+ it('should have a hasDevices function', (done) => {
1299
+ try {
1300
+ assert.equal(true, typeof a.hasDevices === 'function');
1301
+ done();
1302
+ } catch (error) {
1303
+ log.error(`Test Failure: ${error}`);
1304
+ done(error);
1305
+ }
1306
+ });
1307
+ });
1308
+
1309
+ describe('#getDevice', () => {
1310
+ it('should have a getDevice function', (done) => {
1311
+ try {
1312
+ assert.equal(true, typeof a.getDevice === 'function');
1313
+ done();
1314
+ } catch (error) {
1315
+ log.error(`Test Failure: ${error}`);
1316
+ done(error);
1317
+ }
1318
+ });
1319
+ });
1320
+
1321
+ describe('#getDevicesFiltered', () => {
1322
+ it('should have a getDevicesFiltered function', (done) => {
1323
+ try {
1324
+ assert.equal(true, typeof a.getDevicesFiltered === 'function');
1325
+ done();
1326
+ } catch (error) {
1327
+ log.error(`Test Failure: ${error}`);
1328
+ done(error);
1329
+ }
1330
+ });
1331
+ });
1332
+
1333
+ describe('#isAlive', () => {
1334
+ it('should have a isAlive function', (done) => {
1335
+ try {
1336
+ assert.equal(true, typeof a.isAlive === 'function');
1337
+ done();
1338
+ } catch (error) {
1339
+ log.error(`Test Failure: ${error}`);
1340
+ done(error);
1341
+ }
1342
+ });
1343
+ });
1344
+
1345
+ describe('#getConfig', () => {
1346
+ it('should have a getConfig function', (done) => {
1347
+ try {
1348
+ assert.equal(true, typeof a.getConfig === 'function');
1349
+ done();
1350
+ } catch (error) {
1351
+ log.error(`Test Failure: ${error}`);
1352
+ done(error);
1353
+ }
1354
+ });
1355
+ });
1356
+
1357
+ describe('#getCount', () => {
1358
+ it('should have a getCount function', (done) => {
1359
+ try {
1360
+ assert.equal(true, typeof a.getCount === 'function');
1361
+ done();
1362
+ } catch (error) {
1363
+ log.error(`Test Failure: ${error}`);
1364
+ done(error);
1365
+ }
1366
+ });
1367
+ });
1368
+
1270
1369
  /*
1271
1370
  -----------------------------------------------------------------------
1272
1371
  -----------------------------------------------------------------------
@@ -0,0 +1,90 @@
1
+ const fs = require('fs');
2
+ const semverSatisfies = require('semver/functions/satisfies');
3
+ const packageJson = require('../package.json');
4
+
5
+ try {
6
+ // pattern supplied by semver.org via https://regex101.com/r/vkijKf/1/ but removed gm from end to only match a single semver
7
+ // const semverPat = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
8
+ // pattern supplied by semver.org via https://regex101.com/r/Ly7O1x/3/ with following changes
9
+ // removed P's from before capturing group names and
10
+ // removed gm from end to only match a single semver
11
+ // const semverPat = /^(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?<patch>0|[1-9]\d*)(?:-(?<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
12
+
13
+ const patches = (fs.existsSync('./patches')) ? fs.readdirSync('./patches', { withFileTypes: true }) : [];
14
+ if (!patches.length) {
15
+ console.error('\nno patches - nothing to do\n');
16
+ process.exitCode = 1;
17
+ }
18
+
19
+ const dependencies = packageJson.dependencies || {};
20
+ if (!Object.keys(dependencies).length) {
21
+ console.error('\nno dependencies - nothing to do\n');
22
+ process.exitCode = 1;
23
+ }
24
+
25
+ let changed = false;
26
+ console.error('\nprocessing patches');
27
+ const bundledDependencies = packageJson.bundledDependencies || packageJson.bundleDependencies || [];
28
+
29
+ patches.forEach((patch) => {
30
+ if (!patch.isFile()) {
31
+ console.error(`${patch.name} skipped, is not a regular file`);
32
+ return;
33
+ }
34
+ if (!patch.name.endsWith('.patch')) {
35
+ console.error(`${patch.name} skipped, does not end with .patch`);
36
+ return;
37
+ }
38
+ const splits = patch.name.slice(0, -6).split('+');
39
+ if (splits.length > 4) {
40
+ console.error(`${patch.name} skipped, does not follow the naming convention (cannot use '+' other than to separate scope/package/semver and at most once within semver)`);
41
+ return;
42
+ }
43
+ const scope = splits[0][0] === '@' ? splits.shift() : null;
44
+ const packageName = splits.shift();
45
+ const semver = splits.join('+');
46
+ // const { groups } = semver.match(semverPat);
47
+ const file = scope ? `${scope}/${packageName}` : packageName;
48
+ if (dependencies[file] && semverSatisfies(semver, dependencies[file])) {
49
+ if (!bundledDependencies.includes(file)) {
50
+ bundledDependencies.push(file);
51
+ console.error(`added ${file} to bundledDependencies`);
52
+ changed = true;
53
+ } else {
54
+ console.error(`bundledDependencies already has ${file}`);
55
+ }
56
+ } else {
57
+ const depmsg = dependencies[file] ? `version mismatch (${dependencies[file]}) in dependencies` : 'not found in dependencies';
58
+ console.error(`patch ${patch.name} ${depmsg}`);
59
+ }
60
+ });
61
+
62
+ if (!packageJson.bundledDependencies && bundledDependencies.length) {
63
+ delete packageJson.bundleDependencies;
64
+ packageJson.bundledDependencies = bundledDependencies;
65
+ console.error('renaming bundleDependencies to bundledDependencies');
66
+ changed = true;
67
+ }
68
+ if (changed) {
69
+ fs.writeFileSync('./package.json.new', JSON.stringify(packageJson, null, 2));
70
+ console.error('wrote package.json.new');
71
+ fs.renameSync('./package.json', './package.json.old');
72
+ console.error('moved package.json to package.json.old');
73
+ fs.renameSync('./package.json.new', './package.json');
74
+ console.error('moved package.json.new to package.json');
75
+ } else {
76
+ console.error('no changes\n');
77
+ process.exitCode = 1;
78
+ }
79
+ } catch (e) {
80
+ if (e) {
81
+ // caught error, exit with status 2 to signify abject failure
82
+ console.error(`\ncaught exception - ${e}\n`);
83
+ process.exitCode = 2;
84
+ } else {
85
+ // caught false, exit with status 1 to signify nothing done
86
+ process.exitCode = 1;
87
+ }
88
+ } finally {
89
+ console.error('done\n');
90
+ }
package/utils/tbScript.js CHANGED
@@ -21,7 +21,7 @@ const { troubleshoot, offline } = require('./troubleshootingAdapter');
21
21
 
22
22
  const main = async (command) => {
23
23
  const dirname = utils.getDirname();
24
- const iapDir = path.join(dirname, '../../../../');
24
+ const iapDir = path.join(dirname, '../../../');
25
25
  if (!utils.withinIAP(iapDir)) {
26
26
  if (command === 'install') {
27
27
  console.log('Not currently in IAP directory - installation not possible');
package/utils/tbUtils.js CHANGED
@@ -100,8 +100,8 @@ module.exports = {
100
100
  *
101
101
  * @function decryptProperties
102
102
  */
103
- decryptProperties: (props, dirname, discovery) => {
104
- const propertyEncryptionClassPath = path.join(dirname, '../../../@itential/pronghorn-core/core/PropertyEncryption.js');
103
+ decryptProperties: (props, iapDir, discovery) => {
104
+ const propertyEncryptionClassPath = path.join(iapDir, 'node_modules/@itential/pronghorn-core/core/PropertyEncryption.js');
105
105
  const isEncrypted = props.pathProps.encrypted;
106
106
  const PropertyEncryption = discovery.require(propertyEncryptionClassPath, isEncrypted);
107
107
  const propertyEncryption = new PropertyEncryption({
@@ -177,12 +177,12 @@ module.exports = {
177
177
  verifyInstallationDir: (dirname, name) => {
178
178
  const pathArray = dirname.split(path.sep);
179
179
  const expectedPath = `node_modules/${name}`;
180
- const currentPath = pathArray.slice(pathArray.length - 4, pathArray.length - 1).join('/');
180
+ const currentPath = pathArray.slice(pathArray.length - 3, pathArray.length).join('/');
181
181
  if (currentPath.trim() !== expectedPath.trim()) {
182
- throw new Error(`adapter should be installed under ${expectedPath}`);
182
+ throw new Error(`adapter should be installed under ${expectedPath} but is installed under ${currentPath}`);
183
183
  }
184
184
 
185
- const serverFile = path.join(dirname, '../../../..', 'server.js');
185
+ const serverFile = path.join(dirname, '../../../', 'server.js');
186
186
  if (!fs.existsSync(serverFile)) {
187
187
  throw new Error(`adapter should be installed under IAP/${expectedPath}`);
188
188
  }
@@ -345,17 +345,23 @@ module.exports = {
345
345
  console.log('Decrypting properties...');
346
346
  const { Discovery } = require(path.join(iapDir, 'node_modules/@itential/itential-utils'));
347
347
  const discovery = new Discovery();
348
- const pronghornProps = this.decryptProperties(rawProps, this.getDirname(), discovery);
348
+ const pronghornProps = this.decryptProperties(rawProps, iapDir, discovery);
349
349
  console.log('Found properties.\n');
350
350
  return pronghornProps;
351
351
  },
352
352
 
353
353
  // get database connection and existing adapter config
354
354
  getAdapterConfig: async function getAdapterConfig() {
355
- const iapDir = path.join(this.getDirname(), '../../../../');
355
+ const newDirname = this.getDirname();
356
+ let iapDir;
357
+ if (this.withinIAP(newDirname)) { // when this script is called from IAP
358
+ iapDir = newDirname;
359
+ } else {
360
+ iapDir = path.join(this.getDirname(), 'utils', '../../../../');
361
+ }
356
362
  const pronghornProps = this.getPronghornProps(iapDir);
357
363
  console.log('Connecting to Database...');
358
- const database = await this.connect(pronghornProps);
364
+ const database = await this.connect(iapDir, pronghornProps);
359
365
  console.log('Connection established.');
360
366
  const { name } = require(path.join(__dirname, '..', 'package.json'));
361
367
  const serviceItem = await database.collection(this.SERVICE_CONFIGS_COLLECTION).findOne(
@@ -425,7 +431,7 @@ module.exports = {
425
431
  return __dirname;
426
432
  }
427
433
  const { stdout } = this.systemSync('pwd', true);
428
- return path.join(stdout, 'utils');
434
+ return stdout.trim();
429
435
  },
430
436
 
431
437
  /**
@@ -434,9 +440,9 @@ module.exports = {
434
440
  * @function connect
435
441
  * @param {Object} properties - pronghornProps
436
442
  */
437
- connect: async function connect(properties) {
443
+ connect: async function connect(iapDir, properties) {
438
444
  // Connect to Mongo
439
- const { MongoDBConnection } = require(path.join(this.getDirname(), '../../../', '@itential/database'));
445
+ const { MongoDBConnection } = require(path.join(iapDir, 'node_modules/@itential/database'));
440
446
  const connection = new MongoDBConnection(properties.mongoProps);
441
447
  const database = await connection.connect(true);
442
448
  return database;