@itentialopensource/adapter-gcp_compute 1.0.0 → 1.0.1

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.0.1 [12-21-2021]
3
+
4
+ * Don't call readPropsKeyFile in the constructor - if already have a key
5
+
6
+ See merge request itentialopensource/adapters/cloud/adapter-gcp_compute!4
7
+
8
+ ---
9
+
2
10
  ## 1.0.0 [07-28-2021]
3
11
 
4
12
  * add Firewall Policies calls
package/adapter.js CHANGED
@@ -34,7 +34,7 @@ class GcpCompute extends AdapterBaseCl {
34
34
 
35
35
  // for gtoken documentation see https://googleapis.dev/nodejs/gtoken/latest/index.html
36
36
  this.configureGtokenFromProps();
37
- this.keyObj = this.readPropsKeyFile();
37
+ // this.keyObj = this.readPropsKeyFile(); // jorrok 2021-12-20 this method shoud not be called if we have a key; but as it stands, healthCheck depends on keyObj :-(
38
38
  // log.debug(`constructor keyObj is ${JSON.stringify(this.keyObj)}`)
39
39
 
40
40
  // const restFunctionNames = this.getWorkflowFunctions();
@@ -128,7 +128,8 @@ class GcpCompute extends AdapterBaseCl {
128
128
  'refreshAccessTokenPromise',
129
129
  'refreshAuthorizationHeader',
130
130
  'refreshAuthorizationAndIdentifyRequest',
131
- 'getToken'
131
+ 'getToken',
132
+ 'generateKeyObjFromProps'
132
133
  ];
133
134
  if (!inIgnore && Array.isArray(inIgnore)) {
134
135
  myIgnore = inIgnore;
@@ -602,12 +603,35 @@ class GcpCompute extends AdapterBaseCl {
602
603
  return args.reduce((subObj, level) => subObj && subObj[level], obj);
603
604
  }
604
605
 
605
- readPropsKeyFile() {
606
+ generateKeyObjFromProps(someProps) {
607
+ // the blank object comes from https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating
608
+ // the only properties you should be supplying (at most) are the ones with hyphenated dummy values
609
+ // ie: project_id, private_key_id, private_key, client_email, client_id, client_x509_cert_url
610
+ // of which as of ADAPT-1898 only project_id is used (for the adapter's own healthCheck)
611
+ const blank = {
612
+ type: 'service_account',
613
+ project_id: 'project-id',
614
+ private_key_id: 'key-id',
615
+ private_key: '-----BEGIN PRIVATE KEY-----\nprivate-key\n-----END PRIVATE KEY-----\n',
616
+ client_email: 'service-account-email',
617
+ client_id: 'client-id',
618
+ auth_uri: 'https://accounts.google.com/o/oauth2/auth',
619
+ token_uri: 'https://accounts.google.com/o/oauth2/token',
620
+ auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs',
621
+ client_x509_cert_url: 'https://www.googleapis.com/robot/v1/metadata/x509/service-account-email'
622
+ };
623
+ this.keyObj = Object.keys(blank).reduce((gen, key) => ({
624
+ ...gen,
625
+ [key]: someProps.hasOwnProperty(key) ? someProps[key] : blank[key] // eslint-disable-line no-prototype-builtins
626
+ }), {});
627
+ }
628
+
629
+ readPropsKeyFile(keyFile) {
606
630
  const meth = 'adapter-readPropsKeyFile';
607
631
  const origin = `${this.id}-${meth}`;
608
632
  log.trace(origin);
609
633
  try {
610
- const keyFile = this.getNested(this, 'allProps', 'authentication', 'configureGtoken', 'keyFile') || process.env.GOOGLE_APPLICATION_CREDENTIALS;
634
+ // const keyFile = this.getNested(this, 'allProps', 'authentication', 'configureGtoken', 'keyFile') || process.env.GOOGLE_APPLICATION_CREDENTIALS;
611
635
  // log.debug(`readPropsKeyFile keyFile is ${keyFile}`)
612
636
  if (!keyFile) {
613
637
  throw new Error('No keyFile specified in Service Config or GOOGLE_APPLICATION_CREDENTIALS');
@@ -633,7 +657,10 @@ class GcpCompute extends AdapterBaseCl {
633
657
  const configureGtoken = this.getNested(this, 'allProps', 'authentication', 'configureGtoken') || {};
634
658
  if (!configureGtoken.key) {
635
659
  configureGtoken.keyFile = configureGtoken.keyFile || process.env.GOOGLE_APPLICATION_CREDENTIALS || null;
660
+ this.keyObj = this.readPropsKeyFile(configureGtoken.keyFile);
636
661
  } else {
662
+ const { project_id } = configureGtoken; // eslint-disable-line camelcase
663
+ this.generateKeyObjFromProps({ project_id }); // eslint-disable-line camelcase
637
664
  delete configureGtoken.keyFile;
638
665
  }
639
666
  if (configureGtoken.key || configureGtoken.keyFile) {
@@ -738,6 +765,7 @@ class GcpCompute extends AdapterBaseCl {
738
765
  if (kfc && kfc.private_key) {
739
766
  keyFile = null; // eslint-disable-line no-param-reassign
740
767
  key = kfc.private_key; // eslint-disable-line no-param-reassign
768
+ this.keyObj = kfc;
741
769
  } else {
742
770
  throw new Error('Did not obtain private_key from keyFileContents');
743
771
  }
@@ -760,6 +788,7 @@ class GcpCompute extends AdapterBaseCl {
760
788
  log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
761
789
  return callback(null, errorObj);
762
790
  }
791
+ if (keyFile) this.keyObj = this.readPropsKeyFile(keyFile);
763
792
 
764
793
  // build the options for GoogleToken and call it
765
794
  const gOptions = Array.from(arguments).reduce((r, v, i) => { // eslint-disable-line prefer-rest-params
package/adapterBase.js CHANGED
@@ -22,6 +22,7 @@ const AjvCl = require('ajv');
22
22
  const PropUtilCl = require('@itentialopensource/adapter-utils').PropertyUtility;
23
23
  const RequestHandlerCl = require('@itentialopensource/adapter-utils').RequestHandler;
24
24
 
25
+ const entitiesToDB = require(path.join(__dirname, 'utils/entitiesToDB'));
25
26
  const troubleshootingAdapter = require(path.join(__dirname, 'utils/troubleshootingAdapter'));
26
27
  const tbUtils = require(path.join(__dirname, 'utils/tbUtils'));
27
28
 
@@ -1001,6 +1002,27 @@ class AdapterBase extends EventEmitterCl {
1001
1002
  return [];
1002
1003
  }
1003
1004
  }
1005
+
1006
+ /**
1007
+ * @summary moves entities to mongo database
1008
+ *
1009
+ * @function moveEntitiesToDB
1010
+ *
1011
+ * @return {Callback} - containing the response from the mongo transaction
1012
+ */
1013
+ moveEntitiesToDB(callback) {
1014
+ const meth = 'adapterBase-moveEntitiesToDB';
1015
+ const origin = `${this.id}-${meth}`;
1016
+ log.trace(origin);
1017
+
1018
+ try {
1019
+ return callback(entitiesToDB.moveEntitiesToDB(__dirname, { pronghornProps: this.allProps, id: this.id }), null);
1020
+ } catch (err) {
1021
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, err);
1022
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
1023
+ return callback(null, errorObj);
1024
+ }
1025
+ }
1004
1026
  }
1005
1027
 
1006
1028
  module.exports = AdapterBase;
@@ -4,7 +4,7 @@
4
4
  "name": "getGenerics",
5
5
  "protocol": "REST",
6
6
  "method": "GET",
7
- "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}?{query}",
7
+ "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}/{pathv11}/{pathv12}/{pathv13}/{pathv14}/{pathv15}/{pathv16}/{pathv17}/{pathv18}/{pathv19}/{pathv20}?{query}",
8
8
  "requestSchema": "schema.json",
9
9
  "responseSchema": "schema.json",
10
10
  "timeout": 0,
@@ -25,7 +25,7 @@
25
25
  "name": "createGeneric",
26
26
  "protocol": "REST",
27
27
  "method": "POST",
28
- "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}?{query}",
28
+ "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}/{pathv11}/{pathv12}/{pathv13}/{pathv14}/{pathv15}/{pathv16}/{pathv17}/{pathv18}/{pathv19}/{pathv20}?{query}",
29
29
  "requestSchema": "schema.json",
30
30
  "responseSchema": "schema.json",
31
31
  "timeout": 0,
@@ -46,7 +46,7 @@
46
46
  "name": "updateGeneric",
47
47
  "protocol": "REST",
48
48
  "method": "PUT",
49
- "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}?{query}",
49
+ "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}/{pathv11}/{pathv12}/{pathv13}/{pathv14}/{pathv15}/{pathv16}/{pathv17}/{pathv18}/{pathv19}/{pathv20}?{query}",
50
50
  "requestSchema": "schema.json",
51
51
  "responseSchema": "schema.json",
52
52
  "timeout": 0,
@@ -67,7 +67,7 @@
67
67
  "name": "patchGeneric",
68
68
  "protocol": "REST",
69
69
  "method": "PATCH",
70
- "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}?{query}",
70
+ "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}/{pathv11}/{pathv12}/{pathv13}/{pathv14}/{pathv15}/{pathv16}/{pathv17}/{pathv18}/{pathv19}/{pathv20}?{query}",
71
71
  "requestSchema": "schema.json",
72
72
  "responseSchema": "schema.json",
73
73
  "timeout": 0,
@@ -88,7 +88,7 @@
88
88
  "name": "deleteGeneric",
89
89
  "protocol": "REST",
90
90
  "method": "DELETE",
91
- "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}?{query}",
91
+ "entitypath": "{base_path}/{version}/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}/{pathv11}/{pathv12}/{pathv13}/{pathv14}/{pathv15}/{pathv16}/{pathv17}/{pathv18}/{pathv19}/{pathv20}?{query}",
92
92
  "requestSchema": "schema.json",
93
93
  "responseSchema": "schema.json",
94
94
  "timeout": 0,
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@itentialopensource/adapter-gcp_compute",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "This adapter integrates with system described as: computeEngineApi.",
5
5
  "main": "adapter.js",
6
- "wizardVersion": "2.44.0",
7
- "engineVersion": "1.59.28",
6
+ "wizardVersion": "2.44.7",
7
+ "engineVersion": "1.59.50",
8
8
  "adapterType": "http",
9
9
  "scripts": {
10
10
  "artifactize": "npm i && node utils/packModificationScript.js",
11
- "preinstall": "node utils/setup.js",
11
+ "preinstall": "node utils/setup.js && npm install --package-lock-only --ignore-scripts && npx npm-force-resolutions",
12
12
  "lint": "node --max_old_space_size=4096 ./node_modules/eslint/bin/eslint.js . --ext .json --ext .js",
13
13
  "lint:errors": "node --max_old_space_size=4096 ./node_modules/eslint/bin/eslint.js . --ext .json --ext .js --quiet",
14
14
  "test:baseunit": "mocha test/unit/adapterBaseTestUnit.js --LOG=error",
@@ -16,7 +16,7 @@
16
16
  "test:integration": "mocha test/integration/adapterTestIntegration.js --LOG=error",
17
17
  "test:cover": "nyc --reporter html --reporter text mocha --reporter dot test/*",
18
18
  "test": "npm run test:baseunit && npm run test:unit && npm run test:integration",
19
- "adapter:install": "node utils/tbScript.js install",
19
+ "adapter:install": "npm i && node utils/tbScript.js install",
20
20
  "adapter:checkMigrate": "node utils/checkMigrate.js",
21
21
  "adapter:findPath": "node utils/findPath.js",
22
22
  "adapter:migrate": "node utils/modify.js -m",
@@ -26,7 +26,7 @@
26
26
  "healthcheck": "node utils/tbScript.js healthcheck",
27
27
  "basicget": "node utils/tbScript.js basicget",
28
28
  "connectivity": "node utils/tbScript.js connectivity",
29
- "deploy": "npm publish --registry=http://registry.npmjs.org --access=public",
29
+ "deploy": "npm publish --registry=https://registry.npmjs.org --access=public",
30
30
  "build": "npm run deploy"
31
31
  },
32
32
  "keywords": [
@@ -54,7 +54,7 @@
54
54
  "author": "Itential",
55
55
  "homepage": "https://gitlab.com/itentialopensource/adapters/cloud/adapter-gcp_compute#readme",
56
56
  "dependencies": {
57
- "@itentialopensource/adapter-utils": "^4.44.0",
57
+ "@itentialopensource/adapter-utils": "^4.44.11",
58
58
  "ajv": "^6.12.0",
59
59
  "axios": "^0.21.0",
60
60
  "commander": "^2.20.0",
@@ -62,6 +62,7 @@
62
62
  "gtoken": "^5.2.1",
63
63
  "mocha": "^9.0.1",
64
64
  "mocha-param": "^2.0.1",
65
+ "mongodb": "^4.1.0",
65
66
  "network-diagnostics": "^0.5.3",
66
67
  "nyc": "^15.1.0",
67
68
  "readline-sync": "^1.4.10",
@@ -77,5 +78,8 @@
77
78
  "package-json-validator": "^0.6.3",
78
79
  "testdouble": "^3.16.1"
79
80
  },
81
+ "resolutions": {
82
+ "minimist": "^1.2.5"
83
+ },
80
84
  "private": false
81
85
  }
Binary file
@@ -317,7 +317,7 @@ describe('[unit] Adapter Base Test', () => {
317
317
  });
318
318
  it('should return a list of functions', (done) => {
319
319
  const returnedFunctions = ['addEntityCache', 'capabilityResults', 'checkActionFiles', 'checkProperties', 'connect', 'encryptProperty',
320
- 'entityInList', 'findPath', 'getAllCapabilities', 'getAllFunctions', 'getQueue', 'getWorkflowFunctions', 'healthCheck',
320
+ 'entityInList', 'findPath', 'getAllCapabilities', 'getAllFunctions', 'getQueue', 'getWorkflowFunctions', 'healthCheck', 'moveEntitiesToDB',
321
321
  'refreshProperties', 'runBasicGet', 'runConnectivity', 'runHealthcheck', 'suspend', 'troubleshoot', 'unsuspend', 'updateAdapterConfiguration', 'addListener',
322
322
  'emit', 'eventNames', 'getMaxListeners', 'listenerCount', 'listeners', 'off', 'on', 'once', 'prependListener',
323
323
  'prependOnceListener', 'rawListeners', 'removeAllListeners', 'removeListener', 'setMaxListeners'];
@@ -362,7 +362,7 @@ describe('[unit] GcpCompute Adapter Test', () => {
362
362
  assert.notEqual(undefined, packageDotJson.scripts);
363
363
  assert.notEqual(null, packageDotJson.scripts);
364
364
  assert.notEqual('', packageDotJson.scripts);
365
- assert.equal('node utils/setup.js', packageDotJson.scripts.preinstall);
365
+ assert.equal('node utils/setup.js && npm install --package-lock-only --ignore-scripts && npx npm-force-resolutions', packageDotJson.scripts.preinstall);
366
366
  assert.equal('node --max_old_space_size=4096 ./node_modules/eslint/bin/eslint.js . --ext .json --ext .js', packageDotJson.scripts.lint);
367
367
  assert.equal('node --max_old_space_size=4096 ./node_modules/eslint/bin/eslint.js . --ext .json --ext .js --quiet', packageDotJson.scripts['lint:errors']);
368
368
  assert.equal('mocha test/unit/adapterBaseTestUnit.js --LOG=error', packageDotJson.scripts['test:baseunit']);
@@ -370,7 +370,7 @@ describe('[unit] GcpCompute Adapter Test', () => {
370
370
  assert.equal('mocha test/integration/adapterTestIntegration.js --LOG=error', packageDotJson.scripts['test:integration']);
371
371
  assert.equal('nyc --reporter html --reporter text mocha --reporter dot test/*', packageDotJson.scripts['test:cover']);
372
372
  assert.equal('npm run test:baseunit && npm run test:unit && npm run test:integration', packageDotJson.scripts.test);
373
- assert.equal('npm publish --registry=http://registry.npmjs.org --access=public', packageDotJson.scripts.deploy);
373
+ assert.equal('npm publish --registry=https://registry.npmjs.org --access=public', packageDotJson.scripts.deploy);
374
374
  assert.equal('npm run deploy', packageDotJson.scripts.build);
375
375
  done();
376
376
  } catch (error) {
@@ -0,0 +1,94 @@
1
+ /* eslint-disable no-plusplus */
2
+ /* eslint global-require: warn */
3
+ /* eslint import/no-dynamic-require: warn */
4
+
5
+ const rls = require('readline-sync');
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+
9
+ function getQuestions(props, obj) {
10
+ const questions = props.map((p) => `${p}: ${(obj[p] !== undefined) ? `(${obj[p]})` : ''} `);
11
+ return questions;
12
+ }
13
+
14
+ // function outputs each property for user to edit/confirm
15
+ // props are the fields that need to be changed depending on what the user selects
16
+ // obj is the JSON object that's being updated
17
+ function confirm(props, obj) {
18
+ // create array of questions
19
+ const updatedObj = obj;
20
+ getQuestions(props, obj).forEach((q) => {
21
+ const answer = rls.question(q);
22
+ // only update the field if the answer is NOT and empty string
23
+ if (answer) {
24
+ updatedObj[q.split(':')[0].trim()] = answer;
25
+ }
26
+ });
27
+ return updatedObj;
28
+ }
29
+
30
+ const updateBasicAuth = (auth) => {
31
+ const propsToUpdate = ['username', 'password', 'auth_field', 'auth_field_format'];
32
+ return confirm(propsToUpdate, auth);
33
+ };
34
+
35
+ const updateStaticTokenAuth = (auth) => {
36
+ const propsToUpdate = ['token', 'auth_field', 'auth_field_format'];
37
+ return confirm(propsToUpdate, auth);
38
+ };
39
+
40
+ function updateTokenSchemas(user, pw, token) {
41
+ let schemaPath = path.join(__dirname, '..', 'entities/.system/schemaTokenReq.json');
42
+ const reqSchema = require(schemaPath);
43
+ reqSchema.properties.username.external_name = user;
44
+ reqSchema.properties.password.external_name = pw;
45
+ fs.writeFileSync(schemaPath, JSON.stringify(reqSchema, null, 2));
46
+ schemaPath = path.join(__dirname, '..', 'entities/.system/schemaTokenResp.json');
47
+ const respSchema = require(schemaPath);
48
+ respSchema.properties.token.external_name = token;
49
+ fs.writeFileSync(schemaPath, JSON.stringify(respSchema, null, 2));
50
+ }
51
+
52
+ function updateRequestToken(auth) {
53
+ const propsToUpdate = [
54
+ 'username',
55
+ 'password',
56
+ 'auth_field',
57
+ 'auth_field_format',
58
+ 'token_user_field',
59
+ 'token_password_field',
60
+ 'token_result_field',
61
+ 'token_URI_path'
62
+ ];
63
+ const newAuth = confirm(propsToUpdate, auth);
64
+ updateTokenSchemas(newAuth.token_user_field, newAuth.token_password_field, newAuth.token_result_field);
65
+
66
+ return newAuth;
67
+ }
68
+
69
+ // prompt users to pick an auth method from the list above
70
+ const addAuthInfo = (props) => {
71
+ const authOptions = [
72
+ 'basic user_password',
73
+ 'static_token',
74
+ 'request_token',
75
+ 'no_authentication'
76
+ ];
77
+ const newProps = confirm(['host', 'port', 'base_path'], props);
78
+
79
+ const newAuthMethod = authOptions[rls.keyInSelect(authOptions, 'Which authentication method?')];
80
+ newProps.authentication.auth_method = newAuthMethod;
81
+
82
+ if (newAuthMethod === 'basic user_password') {
83
+ newProps.authentication = updateBasicAuth(newProps.authentication);
84
+ } else if (newAuthMethod === 'static_token') {
85
+ newProps.authentication = updateStaticTokenAuth(newProps.authentication);
86
+ } else if (newAuthMethod === 'request_token') {
87
+ newProps.authentication = updateRequestToken(newProps.authentication);
88
+ }
89
+ console.log('Connectivity and authentication properties have been configured');
90
+ console.log('If you want to make changes, rerun this script to reinstall the adapter');
91
+ return newProps;
92
+ };
93
+
94
+ module.exports = { addAuthInfo };
package/utils/basicGet.js CHANGED
@@ -4,6 +4,7 @@
4
4
  /* eslint import/no-extraneous-dependencies: warn */
5
5
  /* eslint global-require: warn */
6
6
  /* eslint import/no-unresolved: warn */
7
+ /* eslint import/no-dynamic-require: warn */
7
8
 
8
9
  const winston = require('winston');
9
10
 
@@ -43,20 +44,6 @@ const basicGet = {
43
44
  adapter.id,
44
45
  adapterProps
45
46
  );
46
- },
47
-
48
- /**
49
- * @summary connect to mongodb
50
- *
51
- * @function connect
52
- * @param {Object} properties - pronghornProps
53
- */
54
- connect: async function connect(properties) {
55
- // Connect to Mongo
56
- const { MongoDBConnection } = require('@itential/database');
57
- const connection = new MongoDBConnection(properties.mongoProps);
58
- const database = await connection.connect(true);
59
- return database;
60
47
  }
61
48
  };
62
49
 
@@ -0,0 +1,224 @@
1
+ /* @copyright Itential, LLC 2021 */
2
+
3
+ // Set globals
4
+ /* global log */
5
+
6
+ /* eslint import/no-dynamic-require: warn */
7
+ /* eslint global-require: warn */
8
+ /* eslint no-unused-vars: warn */
9
+ /* eslint import/no-unresolved: warn */
10
+
11
+ /**
12
+ * This script is used to read through an adapter's entities files
13
+ * and then creates documents and enters them into the IAP mongodb
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const { MongoClient } = require('mongodb');
18
+ const path = require('path');
19
+ // const { argv } = require('process');
20
+ // const { string } = require('yargs');
21
+
22
+ // get the pronghorn database information
23
+ const getPronghornProps = async (iapDir) => {
24
+ log.trace('Retrieving properties.json file...');
25
+ const rawProps = require(path.join(iapDir, 'properties.json'));
26
+ log.trace('Decrypting properties...');
27
+ const { PropertyEncryption } = require('@itential/itential-utils');
28
+ const propertyEncryption = new PropertyEncryption();
29
+ const pronghornProps = await propertyEncryption.decryptProps(rawProps);
30
+ log.trace('Found properties.\n');
31
+ return pronghornProps;
32
+ };
33
+
34
+ /**
35
+ * Function used to take a file path to a entity directory and build
36
+ * a document that corresponds to the entity files.
37
+ */
38
+ const buildDoc = (pathstring) => {
39
+ let files = fs.readdirSync(pathstring);
40
+
41
+ // load the mockdatafiles
42
+ const mockdatafiles = {};
43
+ if (files.includes('mockdatafiles') && fs.lstatSync(`${pathstring}/mockdatafiles`).isDirectory()) {
44
+ fs.readdirSync(`${pathstring}/mockdatafiles`).forEach((file) => {
45
+ if (file.split('.').pop() === 'json') {
46
+ const mockpath = `${pathstring}/mockdatafiles/${file}`;
47
+ const data = JSON.parse(fs.readFileSync(mockpath));
48
+ mockdatafiles[mockpath.split('/').pop()] = data;
49
+ }
50
+ });
51
+ }
52
+
53
+ // load the action data
54
+ let actions;
55
+ if (files.includes('action.json')) {
56
+ actions = JSON.parse(fs.readFileSync(`${pathstring}/action.json`));
57
+ }
58
+
59
+ // Load schema.json and other schemas in remaining json files
60
+ files = files.filter((f) => (f !== 'action.json') && f.endsWith('.json'));
61
+ const schema = [];
62
+ files.forEach((file) => {
63
+ const data = JSON.parse(fs.readFileSync(`${pathstring}/${file}`));
64
+ schema.push({
65
+ name: file,
66
+ schema: data
67
+ });
68
+ });
69
+
70
+ // return the data
71
+ return {
72
+ actions: actions.actions,
73
+ schema,
74
+ mockdatafiles
75
+ };
76
+ };
77
+
78
+ /**
79
+ * Function used to get the database from the options or a provided directory
80
+ */
81
+ const optionsHandler = (options) => {
82
+ // if the database properties were provided in the options - return them
83
+ if (options.pronghornProps) {
84
+ if (typeof options.pronghornProps === 'string') {
85
+ return JSON.parse(options.pronghornProps);
86
+ }
87
+ return new Promise((resolve, reject) => resolve(options.pronghornProps));
88
+ }
89
+
90
+ // if the directory was provided, get the pronghorn props from the directory
91
+ if (options.iapDir) {
92
+ return getPronghornProps(options.iapDir);
93
+ }
94
+
95
+ // if nothing was provided, error
96
+ return new Promise((resolve, reject) => reject(new Error('Neither pronghornProps nor iapDir defined in options!')));
97
+ };
98
+
99
+ /**
100
+ * Function used to put the adapter configuration into the provided database
101
+ */
102
+ const moveEntitiesToDB = (targetPath, options) => {
103
+ // set local variables
104
+ let myOpts = options;
105
+ let myPath = targetPath;
106
+
107
+ // if we got a string parse into a JSON object
108
+ if (typeof myOpts === 'string') {
109
+ myOpts = JSON.parse(myOpts);
110
+ }
111
+
112
+ // if there is no target collection - set the collection to the default
113
+ if (!myOpts.targetCollection) {
114
+ myOpts.targetCollection = 'adapter_configs';
115
+ }
116
+
117
+ // if there is no id error since we need an id for the entities
118
+ if (!myOpts.id) {
119
+ throw new Error('Adapter ID required!');
120
+ }
121
+
122
+ // get the pronghorn database properties
123
+ optionsHandler(options).then((currentProps) => {
124
+ let mongoUrl;
125
+ let dbName;
126
+
127
+ // find the mongo properties so we can connect
128
+ if (currentProps.mongoProps) {
129
+ mongoUrl = currentProps.mongoProps.url;
130
+ dbName = currentProps.mongoProps.db;
131
+ } else if (currentProps.mongo) {
132
+ if (currentProps.mongo.url) {
133
+ mongoUrl = currentProps.mongo.url;
134
+ } else {
135
+ mongoUrl = `mongodb://${currentProps.mongo.host}:${currentProps.mongo.port}`;
136
+ }
137
+ dbName = currentProps.mongo.database;
138
+ } else {
139
+ throw new Error('Mongo properties are not specified in adapter preferences!');
140
+ }
141
+
142
+ // Check valid filepath provided
143
+ if (!myPath) {
144
+ // if no path use the current directory without the utils
145
+ myPath = path.join(__dirname, '../');
146
+ } else if (myPath.slice(-1) === '/') {
147
+ myPath = myPath.slice(0, -1);
148
+ }
149
+
150
+ // verify set the entity path
151
+ const entitiesPath = `${myPath}/entities`;
152
+ if (!fs.existsSync(entitiesPath)) {
153
+ throw new Error(`Entities path does not exist in filesystem: ${entitiesPath}`);
154
+ } else {
155
+ log.trace('Target found on filesystem');
156
+ }
157
+
158
+ // Get adapter details
159
+ if (!fs.existsSync(`${myPath}/pronghorn.json`)) {
160
+ throw new Error(`pronghorn.json does not exist in path: ${myPath}`);
161
+ } else {
162
+ log.trace('pronghorn.json found on filesystem');
163
+ }
164
+ const adapterData = JSON.parse(fs.readFileSync(`${myPath}/pronghorn.json`));
165
+
166
+ // Load files from the filesystem
167
+ const docs = [];
168
+ const entities = fs.readdirSync(entitiesPath);
169
+ entities.forEach((entity) => {
170
+ const entityPath = `${entitiesPath}/${entity}`;
171
+ const isDir = fs.lstatSync(entitiesPath).isDirectory();
172
+
173
+ // Build doc for entity
174
+ if (isDir) {
175
+ let doc = buildDoc(entityPath);
176
+ doc = {
177
+ id: myOpts.id,
178
+ type: adapterData.id,
179
+ entity,
180
+ ...doc
181
+ };
182
+ docs.push(doc);
183
+ }
184
+ });
185
+
186
+ // Upload documents to db collection
187
+ MongoClient.connect(mongoUrl, (err, db) => {
188
+ if (err) {
189
+ log.error(JSON.stringify(err));
190
+ throw err;
191
+ }
192
+
193
+ // get the proper collection
194
+ const collection = db.db(dbName).collection(myOpts.targetCollection);
195
+ // insert the documents into the collection
196
+ collection.insertMany(docs, { checkKeys: false }, (error, res) => {
197
+ if (error) {
198
+ log.error(JSON.stringify(error));
199
+ throw error;
200
+ }
201
+ // log the insertion, close the database and return
202
+ log.debug(`Inserted ${docs.length} documents to ${dbName}.${myOpts.targetCollection} with response ${JSON.stringify(res)}`);
203
+ db.close();
204
+ return res;
205
+ });
206
+ });
207
+ });
208
+ };
209
+
210
+ // const args = process.argv.slice(2);
211
+
212
+ // throw new SyntaxError(args[0]);
213
+
214
+ // if (args.length === 0) {
215
+ // console.error('ERROR: target path not specified!');
216
+ // } else if (args[0] === 'help') {
217
+ // log.trace('node ./entitiesToDB <target path> <options object: {iapDir: string, pronghornProps: string, targetCollection: string}>');
218
+ // } else if (args.length === 1) {
219
+ // console.error('ERROR: IAP directory not specified');
220
+ // } else {
221
+ // moveEntitiesToDB(args[0], args[1]);
222
+ // }
223
+
224
+ module.exports = { moveEntitiesToDB };
package/utils/modify.js CHANGED
@@ -3,7 +3,7 @@ const Ajv = require('ajv');
3
3
  const rls = require('readline-sync');
4
4
  const { execSync } = require('child_process');
5
5
  const { existsSync } = require('fs-extra');
6
- const { getAdapterConfig } = require('./troubleshootingAdapter');
6
+ const { getAdapterConfig } = require('./tbUtils');
7
7
  const { name } = require('../package.json');
8
8
  const propertiesSchema = require('../propertiesSchema.json');
9
9
 
@@ -0,0 +1,20 @@
1
+ const fs = require('fs');
2
+
3
+ /**
4
+ * This script will uninstall pre-commit or pre-push hooks in case there's ever a need to
5
+ * commit/push something that has issues
6
+ */
7
+
8
+ const precommitPath = '.git/hooks/pre-commit';
9
+ const prepushPath = '.git/hooks/pre-push';
10
+ fs.unlink(precommitPath, (err) => {
11
+ if (err && err.code !== 'ENOENT') {
12
+ console.log(`${err.message}`);
13
+ }
14
+ });
15
+
16
+ fs.unlink(prepushPath, (err) => {
17
+ if (err && err.code !== 'ENOENT') {
18
+ console.log(`${err.message}`);
19
+ }
20
+ });
package/utils/tbScript.js CHANGED
@@ -15,13 +15,18 @@ const basicGet = require('./basicGet');
15
15
  const { name } = require('../package.json');
16
16
  const sampleProperties = require('../sampleProperties.json');
17
17
  const adapterPronghorn = require('../pronghorn.json');
18
+ const { addAuthInfo } = require('./addAuth');
18
19
 
19
- const { troubleshoot, getAdapterConfig, offline } = require('./troubleshootingAdapter');
20
+ const { troubleshoot, offline } = require('./troubleshootingAdapter');
20
21
 
21
22
  const main = async (command) => {
22
- const iapDir = path.join(__dirname, '../../../../');
23
+ const dirname = utils.getDirname();
24
+ const iapDir = path.join(dirname, '../../../../');
23
25
  if (!utils.withinIAP(iapDir)) {
24
- if (command === 'connectivity') {
26
+ if (command === 'install') {
27
+ console.log('Not currently in IAP directory - installation not possible');
28
+ process.exit(0);
29
+ } else if (command === 'connectivity') {
25
30
  const { host } = sampleProperties.properties;
26
31
  console.log(`perform networking diagnositics to ${host}`);
27
32
  await utils.runConnectivity(host);
@@ -43,7 +48,7 @@ const main = async (command) => {
43
48
  if (command === undefined) {
44
49
  await troubleshoot({}, true, true);
45
50
  } else if (command === 'install') {
46
- const { database, serviceItem, pronghornProps } = await getAdapterConfig();
51
+ const { database, serviceItem, pronghornProps } = await utils.getAdapterConfig();
47
52
  const filter = { id: pronghornProps.id };
48
53
  const profileItem = await database.collection(utils.IAP_PROFILES_COLLECTION).findOne(filter);
49
54
  if (!profileItem) {
@@ -74,14 +79,16 @@ const main = async (command) => {
74
79
  process.exit(0);
75
80
  }
76
81
  } else {
77
- utils.verifyInstallationDir(__dirname, name);
78
- utils.npmInstall();
82
+ utils.verifyInstallationDir(dirname, name);
79
83
  utils.runTest();
80
84
  if (rls.keyInYN(`Do you want to install ${name} to IAP?`)) {
81
85
  console.log('Creating database entries...');
82
86
  const adapter = utils.createAdapter(
83
87
  pronghornProps, profileItem, sampleProperties, adapterPronghorn
84
88
  );
89
+
90
+ adapter.properties.properties = await addAuthInfo(adapter.properties.properties);
91
+
85
92
  await database.collection(utils.SERVICE_CONFIGS_COLLECTION).insertOne(adapter);
86
93
  profileItem.services.push(adapter.name);
87
94
  const update = { $set: { services: profileItem.services } };
@@ -94,7 +101,7 @@ const main = async (command) => {
94
101
  process.exit(0);
95
102
  }
96
103
  } else if (['healthcheck', 'basicget', 'connectivity'].includes(command)) {
97
- const { serviceItem } = await getAdapterConfig();
104
+ const { serviceItem } = await utils.getAdapterConfig();
98
105
  if (serviceItem) {
99
106
  const adapter = serviceItem;
100
107
  const a = basicGet.getAdapterInstance(adapter);
@@ -154,7 +161,6 @@ program.parse(process.argv);
154
161
  if (process.argv.length < 3) {
155
162
  main();
156
163
  }
157
-
158
164
  const allowedParams = ['install', 'healthcheck', 'basicget', 'connectivity'];
159
165
  if (process.argv.length === 3 && !allowedParams.includes(process.argv[2])) {
160
166
  console.log(`unknown parameter ${process.argv[2]}`);
package/utils/tbUtils.js CHANGED
@@ -178,7 +178,7 @@ module.exports = {
178
178
  const pathArray = dirname.split(path.sep);
179
179
  const expectedPath = `node_modules/${name}`;
180
180
  const currentPath = pathArray.slice(pathArray.length - 4, pathArray.length - 1).join('/');
181
- if (expectedPath !== currentPath) {
181
+ if (currentPath.trim() !== expectedPath.trim()) {
182
182
  throw new Error(`adapter should be installed under ${expectedPath}`);
183
183
  }
184
184
 
@@ -304,8 +304,9 @@ module.exports = {
304
304
  * @param {Object} adapterPronghorn - ./pronghorn.json in adapter dir
305
305
  * @param {Object} sampleProperties - './sampleProperties.json' in adapter dir
306
306
  */
307
- createAdapter: (pronghornProps, profileItem, sampleProperties, adapterPronghorn) => {
308
- const packagePath = `${__dirname.split('node_modules')[0]}package.json`;
307
+ createAdapter: function createAdapter(pronghornProps, profileItem, sampleProperties, adapterPronghorn) {
308
+ const dirname = this.getDirname();
309
+ const packagePath = `${dirname.split('node_modules')[0]}package.json`;
309
310
  const info = JSON.parse(fs.readFileSync(packagePath));
310
311
  const version = parseInt(info.version.split('.')[0], 10);
311
312
 
@@ -338,6 +339,31 @@ module.exports = {
338
339
  return adapter;
339
340
  },
340
341
 
342
+ getPronghornProps: function getPronghornProps(iapDir) {
343
+ console.log('Retrieving properties.json file...');
344
+ const rawProps = require(path.join(iapDir, 'properties.json'));
345
+ console.log('Decrypting properties...');
346
+ const { Discovery } = require(path.join(iapDir, 'node_modules/@itential/itential-utils'));
347
+ const discovery = new Discovery();
348
+ const pronghornProps = this.decryptProperties(rawProps, this.getDirname(), discovery);
349
+ console.log('Found properties.\n');
350
+ return pronghornProps;
351
+ },
352
+
353
+ // get database connection and existing adapter config
354
+ getAdapterConfig: async function getAdapterConfig() {
355
+ const iapDir = path.join(this.getDirname(), '../../../../');
356
+ const pronghornProps = this.getPronghornProps(iapDir);
357
+ console.log('Connecting to Database...');
358
+ const database = await this.connect(pronghornProps);
359
+ console.log('Connection established.');
360
+ const { name } = require(path.join(__dirname, '..', 'package.json'));
361
+ const serviceItem = await database.collection(this.SERVICE_CONFIGS_COLLECTION).findOne(
362
+ { model: name }
363
+ );
364
+ return { database, serviceItem, pronghornProps };
365
+ },
366
+
341
367
  /**
342
368
  * @summary return async healthcheck result as a Promise
343
369
  *
@@ -385,5 +411,35 @@ module.exports = {
385
411
  } catch (error) {
386
412
  return false;
387
413
  }
414
+ },
415
+
416
+ /**
417
+ * @summary Used to determine the proper dirname to return in case adapter reference is
418
+ * symlinked withink IAP
419
+ * @returns the symlinked path (using pwd command) of the adapter in case properties.json
420
+ * is not found in the original path
421
+ * @function getDirname
422
+ */
423
+ getDirname: function getDirname() {
424
+ if (this.withinIAP(path.join(__dirname, '../../../../'))) {
425
+ return __dirname;
426
+ }
427
+ const { stdout } = this.systemSync('pwd', true);
428
+ return path.join(stdout, 'utils');
429
+ },
430
+
431
+ /**
432
+ * @summary connect to mongodb
433
+ *
434
+ * @function connect
435
+ * @param {Object} properties - pronghornProps
436
+ */
437
+ connect: async function connect(properties) {
438
+ // Connect to Mongo
439
+ const { MongoDBConnection } = require(path.join(this.getDirname(), '../../../', '@itential/database'));
440
+ const connection = new MongoDBConnection(properties.mongoProps);
441
+ const database = await connection.connect(true);
442
+ return database;
388
443
  }
444
+
389
445
  };
@@ -117,30 +117,6 @@ const VerifyHealthCheckEndpoint = (serviceItem, props, scriptFlag) => {
117
117
  return { result, updatedAdapter };
118
118
  };
119
119
 
120
- const getPronghornProps = (iapDir) => {
121
- console.log('Retrieving properties.json file...');
122
- const rawProps = require(path.join(iapDir, 'properties.json'));
123
- console.log('Decrypting properties...');
124
- const { Discovery } = require('@itential/itential-utils');
125
- const discovery = new Discovery();
126
- const pronghornProps = utils.decryptProperties(rawProps, __dirname, discovery);
127
- console.log('Found properties.\n');
128
- return pronghornProps;
129
- };
130
-
131
- // get database connection and existing adapter config
132
- const getAdapterConfig = async () => {
133
- const iapDir = path.join(__dirname, '../../../../');
134
- const pronghornProps = getPronghornProps(iapDir);
135
- console.log('Connecting to Database...');
136
- const database = await basicGet.connect(pronghornProps);
137
- console.log('Connection established.');
138
- const serviceItem = await database.collection(utils.SERVICE_CONFIGS_COLLECTION).findOne(
139
- { model: name }
140
- );
141
- return { database, serviceItem, pronghornProps };
142
- };
143
-
144
120
  const offline = async () => {
145
121
  console.log('Start offline troubleshooting');
146
122
  const { updatedAdapter } = VerifyHealthCheckEndpoint({ properties: sampleProperties }, {}, true);
@@ -159,7 +135,7 @@ const offline = async () => {
159
135
 
160
136
  const troubleshoot = async (props, scriptFlag, persistFlag, adapter) => {
161
137
  // get database connection and existing adapter config
162
- const { database, serviceItem } = await getAdapterConfig();
138
+ const { database, serviceItem } = await utils.getAdapterConfig();
163
139
  // where troubleshoot should start
164
140
  if (serviceItem) {
165
141
  if (!scriptFlag || rls.keyInYN(`Start verifying the connection and authentication properties for ${name}?`)) {
@@ -211,4 +187,4 @@ const troubleshoot = async (props, scriptFlag, persistFlag, adapter) => {
211
187
  return null;
212
188
  };
213
189
 
214
- module.exports = { troubleshoot, getAdapterConfig, offline };
190
+ module.exports = { troubleshoot, offline };