@itentialopensource/adapter-meraki 0.7.0 → 0.8.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/.eslintignore +1 -0
- package/.eslintrc.js +12 -12
- package/CHANGELOG.md +32 -0
- package/README.md +270 -68
- package/adapter.js +2786 -24
- package/adapterBase.js +544 -17
- package/entities/.generic/action.json +109 -0
- package/entities/.generic/schema.json +23 -0
- package/entities/.system/action.json +1 -1
- package/entities/CameraQualityRetentionProfiles/action.json +3 -0
- package/entities/ConnectivityMonitoringDestinations/action.json +1 -0
- package/entities/DashboardBrandingPolicies/action.json +4 -0
- package/entities/Floorplans/action.json +3 -0
- package/entities/Licenses/action.json +5 -0
- package/entities/LinkAggregations/action.json +3 -0
- package/entities/MGConnectivityMonitoringDestinations/action.json +1 -0
- package/entities/MGDHCPSettings/action.json +1 -0
- package/entities/MGLANSettings/action.json +1 -0
- package/entities/MGPortforwardingRules/action.json +1 -0
- package/entities/MGSubnetPoolSettings/action.json +1 -0
- package/entities/MGUplinkSettings/action.json +1 -0
- package/entities/MXVLANPorts/action.json +1 -0
- package/entities/MXWarmSpareSettings/action.json +2 -0
- package/entities/NetFlowSettings/action.json +1 -0
- package/entities/Switch settings/action.json +9 -0
- package/entities/SwitchACLs/action.json +1 -0
- package/entities/SwitchPortsSchedules/action.json +3 -0
- package/entities/TrafficAnalysisSettings/action.json +1 -0
- package/entities/WirelessSettings/action.json +1 -0
- package/error.json +6 -0
- package/package.json +45 -23
- package/pronghorn.json +586 -16
- package/propertiesSchema.json +84 -11
- package/refs?service=git-upload-pack +0 -0
- package/report/meraki-newcalls-OpenApi3Json.json +5460 -0
- package/report/updateReport1594225126093.json +95 -0
- package/report/updateReport1615384306128.json +95 -0
- package/report/updateReport1642739939352.json +95 -0
- package/sampleProperties.json +20 -5
- package/test/integration/adapterTestBasicGet.js +85 -0
- package/test/integration/adapterTestConnectivity.js +93 -0
- package/test/integration/adapterTestIntegration.js +30 -11
- package/test/unit/adapterBaseTestUnit.js +944 -0
- package/test/unit/adapterTestUnit.js +638 -12
- package/utils/addAuth.js +94 -0
- package/utils/artifactize.js +9 -14
- package/utils/basicGet.js +50 -0
- package/utils/checkMigrate.js +63 -0
- package/utils/entitiesToDB.js +224 -0
- package/utils/findPath.js +74 -0
- package/utils/modify.js +154 -0
- package/utils/packModificationScript.js +1 -1
- package/utils/patches2bundledDeps.js +90 -0
- package/utils/pre-commit.sh +1 -1
- package/utils/removeHooks.js +20 -0
- package/utils/tbScript.js +169 -0
- package/utils/tbUtils.js +451 -0
- package/utils/troubleshootingAdapter.js +190 -0
- package/gl-code-quality-report.json +0 -1
package/utils/tbUtils.js
ADDED
@@ -0,0 +1,451 @@
|
|
1
|
+
/* @copyright Itential, LLC 2020 */
|
2
|
+
|
3
|
+
/* eslint import/no-extraneous-dependencies: warn */
|
4
|
+
/* eslint global-require: warn */
|
5
|
+
/* eslint import/no-dynamic-require: warn */
|
6
|
+
|
7
|
+
const path = require('path');
|
8
|
+
const fs = require('fs-extra');
|
9
|
+
const cp = require('child_process');
|
10
|
+
|
11
|
+
module.exports = {
|
12
|
+
SERVICE_CONFIGS_COLLECTION: 'service_configs',
|
13
|
+
IAP_PROFILES_COLLECTION: 'iap_profiles',
|
14
|
+
|
15
|
+
/**
|
16
|
+
* @summary update newConnection properties in adapter config
|
17
|
+
*
|
18
|
+
* @function updateNewConnection
|
19
|
+
* @param {Object} config - adaper configuration object required by IAP
|
20
|
+
* @param {Object} newConnection - connection related property collection from user
|
21
|
+
*/
|
22
|
+
updateNewConnection: (config, newConnection) => {
|
23
|
+
const updatedConfig = JSON.parse(JSON.stringify(config));
|
24
|
+
Object.keys(newConnection).forEach((key) => {
|
25
|
+
updatedConfig.properties.properties[key] = newConnection[key];
|
26
|
+
});
|
27
|
+
return updatedConfig;
|
28
|
+
},
|
29
|
+
|
30
|
+
/**
|
31
|
+
* @summary assemble heathcheck endpoint into an URL
|
32
|
+
*
|
33
|
+
* @function getHealthCheckEndpointURL
|
34
|
+
* @param {Object} endpoint - user updated healthcheck endpoint object
|
35
|
+
* @param {Object} config - adaper configuration object required by IAP
|
36
|
+
*/
|
37
|
+
getHealthCheckEndpointURL: (endpoint, config) => {
|
38
|
+
const p = config.properties.properties;
|
39
|
+
const healthCheckEndpointURL = `${p.protocol}://${p.host}${p.base_path}${p.version}${endpoint.healthCheckEndpoint}`;
|
40
|
+
console.log({ healthCheckEndpointURL });
|
41
|
+
return healthCheckEndpointURL;
|
42
|
+
},
|
43
|
+
|
44
|
+
/**
|
45
|
+
* @summary persist healthcheck endpoint when user make update
|
46
|
+
*
|
47
|
+
* @function updateHealthCheckEndpoint
|
48
|
+
* @param {Object} newHealthCheckEndpoint - user confirmed healthcheck object
|
49
|
+
* @param {Object} healthCheckEndpoint - existing healthcheck object
|
50
|
+
* @param {Object} healthcheck - ./entities/.system/action.json object
|
51
|
+
*/
|
52
|
+
updateHealthCheckEndpoint: (newHealthCheckEndpoint, healthCheckEndpoint, healthcheck) => {
|
53
|
+
if (newHealthCheckEndpoint.healthCheckEndpoint !== healthCheckEndpoint.healthCheckEndpoint) {
|
54
|
+
const p = healthcheck.actions[1].entitypath;
|
55
|
+
const newEntitypath = p.slice(0, 21) + newHealthCheckEndpoint.healthCheckEndpoint + p.slice(p.length - 8);
|
56
|
+
const updatedHealthcheck = JSON.parse(JSON.stringify(healthcheck));
|
57
|
+
updatedHealthcheck.actions[1].entitypath = newEntitypath;
|
58
|
+
console.log('updating healthcheck setting');
|
59
|
+
fs.writeFileSync('./entities/.system/action.json', JSON.stringify(updatedHealthcheck, null, 2));
|
60
|
+
}
|
61
|
+
},
|
62
|
+
|
63
|
+
/**
|
64
|
+
* @summary update authentication property given new input value from user
|
65
|
+
* compare values from auth and newAuth, if there's difference
|
66
|
+
* update adapter config
|
67
|
+
* @function updateAuth
|
68
|
+
* @param {Object} newAuth - user confirmed authentication object
|
69
|
+
* @param {Object} auth - existing authentication object
|
70
|
+
* @param {Object} config - adaper configuration object required by IAP
|
71
|
+
*/
|
72
|
+
updateAuth: (newAuth, auth, config) => {
|
73
|
+
const updatedConfig = JSON.parse(JSON.stringify(config));
|
74
|
+
if (Object.keys(newAuth).every((key) => newAuth[key] === auth[key])) {
|
75
|
+
return config;
|
76
|
+
}
|
77
|
+
Object.keys(newAuth).forEach((key) => {
|
78
|
+
updatedConfig.properties.properties.authentication[key] = newAuth[key];
|
79
|
+
});
|
80
|
+
console.log(updatedConfig.properties.properties.authentication);
|
81
|
+
return updatedConfig;
|
82
|
+
},
|
83
|
+
|
84
|
+
/**
|
85
|
+
* @summary add mark current auth_method with `(current)`
|
86
|
+
*
|
87
|
+
* @function getDisplayAuthOptions
|
88
|
+
* @param {String} currentAuth - current auth method in adapter config
|
89
|
+
* @param {Array} authOptions - available auth method
|
90
|
+
*/
|
91
|
+
getDisplayAuthOptions: (currentAuth, authOptions) => {
|
92
|
+
const displayAuthOptions = JSON.parse(JSON.stringify(authOptions));
|
93
|
+
displayAuthOptions[authOptions.indexOf(currentAuth)] += ' (current)';
|
94
|
+
return displayAuthOptions;
|
95
|
+
},
|
96
|
+
|
97
|
+
/**
|
98
|
+
* @summary decrypt IAP properties
|
99
|
+
* code from pronghorn-core/migration_scripts/installService.js
|
100
|
+
*
|
101
|
+
* @function decryptProperties
|
102
|
+
*/
|
103
|
+
decryptProperties: (props, iapDir, discovery) => {
|
104
|
+
const propertyEncryptionClassPath = path.join(iapDir, 'node_modules/@itential/pronghorn-core/core/PropertyEncryption.js');
|
105
|
+
const isEncrypted = props.pathProps.encrypted;
|
106
|
+
const PropertyEncryption = discovery.require(propertyEncryptionClassPath, isEncrypted);
|
107
|
+
const propertyEncryption = new PropertyEncryption({
|
108
|
+
algorithm: 'aes-256-ctr',
|
109
|
+
key: 'TG9uZ0Rpc3RhbmNlUnVubmVyUHJvbmdob3JuCg==',
|
110
|
+
encoding: 'utf-8'
|
111
|
+
});
|
112
|
+
return propertyEncryption.decryptProps(props);
|
113
|
+
},
|
114
|
+
|
115
|
+
/**
|
116
|
+
* @summary create connection object for verification
|
117
|
+
*
|
118
|
+
* @function getConnection
|
119
|
+
* @param {Object} props - adapter config.properties
|
120
|
+
*/
|
121
|
+
getConnection: (props) => {
|
122
|
+
const connection = {
|
123
|
+
host: props.properties.host,
|
124
|
+
base_path: props.properties.base_path,
|
125
|
+
protocol: props.properties.protocol,
|
126
|
+
version: props.properties.version,
|
127
|
+
port: props.properties.port
|
128
|
+
};
|
129
|
+
return connection;
|
130
|
+
},
|
131
|
+
|
132
|
+
/**
|
133
|
+
* @summary update connection properties based on user answer
|
134
|
+
*
|
135
|
+
* @function getNewProps
|
136
|
+
* @param {Array} answers - values collected from CLI
|
137
|
+
* @param {Object} connection - connection property verified by user
|
138
|
+
*/
|
139
|
+
getNewProps: (answers, connection) => {
|
140
|
+
if (answers.every((answer) => answer === '')) {
|
141
|
+
return connection;
|
142
|
+
}
|
143
|
+
const newConnection = {};
|
144
|
+
const properties = Object.keys(connection);
|
145
|
+
for (let i = 0; i < answers.length; i += 1) {
|
146
|
+
if (answers[i]) {
|
147
|
+
newConnection[properties[i]] = Number.isNaN(Number(answers[i])) ? answers[i] : Number(answers[i]);
|
148
|
+
} else {
|
149
|
+
newConnection[properties[i]] = connection[properties[i]];
|
150
|
+
}
|
151
|
+
}
|
152
|
+
return newConnection;
|
153
|
+
},
|
154
|
+
|
155
|
+
/**
|
156
|
+
* @summary extract endpoint string from healthcheck object
|
157
|
+
*
|
158
|
+
* @function getHealthCheckEndpoint
|
159
|
+
* @param {Object} healthcheck - {Object} healthcheck - ./entities/.system/action.json object
|
160
|
+
*/
|
161
|
+
getHealthCheckEndpoint: (healthcheck) => {
|
162
|
+
const endpoint = healthcheck.actions[1].entitypath.slice(21,
|
163
|
+
healthcheck.actions[1].entitypath.length - 8);
|
164
|
+
return { healthCheckEndpoint: endpoint };
|
165
|
+
},
|
166
|
+
|
167
|
+
/**
|
168
|
+
* @summary Verify that the adapter is in the correct directory
|
169
|
+
* - Within IAP
|
170
|
+
* - In node_modules/@ namespace
|
171
|
+
* verify the adapter is installed under node_modules/
|
172
|
+
* and is consistent with the name property of package.json
|
173
|
+
* and the node_modules/ is in the correct path within IAP
|
174
|
+
* @param {String} dirname - current path
|
175
|
+
* @param {String} name - name property from package.json
|
176
|
+
*/
|
177
|
+
verifyInstallationDir: (dirname, name) => {
|
178
|
+
const pathArray = dirname.split(path.sep);
|
179
|
+
const expectedPath = `node_modules/${name}`;
|
180
|
+
const currentPath = pathArray.slice(pathArray.length - 3, pathArray.length).join('/');
|
181
|
+
if (currentPath.trim() !== expectedPath.trim()) {
|
182
|
+
throw new Error(`adapter should be installed under ${expectedPath} but is installed under ${currentPath}`);
|
183
|
+
}
|
184
|
+
|
185
|
+
const serverFile = path.join(dirname, '../../../', 'server.js');
|
186
|
+
if (!fs.existsSync(serverFile)) {
|
187
|
+
throw new Error(`adapter should be installed under IAP/${expectedPath}`);
|
188
|
+
}
|
189
|
+
console.log(`adapter correctly installed at ${currentPath}`);
|
190
|
+
},
|
191
|
+
|
192
|
+
/**
|
193
|
+
* @summary execute command and preserve the output the same as run command in shell
|
194
|
+
*
|
195
|
+
* @function systemSync
|
196
|
+
* @param {String} cmd - Command to execute
|
197
|
+
* @param {boolean} process - Whether stdout should be processed and returned
|
198
|
+
*/
|
199
|
+
systemSync: function systemSync(cmd, process) {
|
200
|
+
if (process) {
|
201
|
+
let stdout;
|
202
|
+
try {
|
203
|
+
stdout = cp.execSync(cmd).toString();
|
204
|
+
} catch (error) {
|
205
|
+
stdout = error.stdout.toString();
|
206
|
+
}
|
207
|
+
const output = this.getTestCount(stdout);
|
208
|
+
output.stdout = stdout;
|
209
|
+
return output;
|
210
|
+
}
|
211
|
+
try {
|
212
|
+
return cp.execSync(cmd, { stdio: 'inherit' });
|
213
|
+
} catch (error) {
|
214
|
+
return console.error(error.stdout);
|
215
|
+
}
|
216
|
+
},
|
217
|
+
|
218
|
+
/**
|
219
|
+
* @summary parses a string and returns the number parsed from startIndex backwards
|
220
|
+
*
|
221
|
+
* @function parseNum
|
222
|
+
* @param {String} inputStr - Any String
|
223
|
+
* @param {Number} startIndex - Index to begin parsing
|
224
|
+
*/
|
225
|
+
parseNum: function parseNum(inputStr, startIndex) {
|
226
|
+
let count = '';
|
227
|
+
let currChar;
|
228
|
+
let start = startIndex;
|
229
|
+
while (currChar !== ' ') {
|
230
|
+
currChar = inputStr.charAt(start);
|
231
|
+
count = currChar + count;
|
232
|
+
start -= 1;
|
233
|
+
}
|
234
|
+
return parseInt(count, 10);
|
235
|
+
},
|
236
|
+
|
237
|
+
/**
|
238
|
+
* @summary Parses a mocha test result and returns the count of passing and failing tests
|
239
|
+
*
|
240
|
+
* @function getTestCount
|
241
|
+
* @param {String} testStr - Output from mocha test
|
242
|
+
*/
|
243
|
+
getTestCount: function getTestCount(testStr) {
|
244
|
+
const passIndex = testStr.search('passing');
|
245
|
+
const failIndex = testStr.search('failing');
|
246
|
+
const passCount = passIndex >= 0 ? this.parseNum(testStr, passIndex - 2) : 0;
|
247
|
+
const failCount = failIndex >= 0 ? this.parseNum(testStr, failIndex - 2) : 0;
|
248
|
+
return { passCount, failCount };
|
249
|
+
},
|
250
|
+
|
251
|
+
/**
|
252
|
+
* @summary remove package-lock.json and node_modules directory if exists
|
253
|
+
* run npm install and print result to stdout
|
254
|
+
*/
|
255
|
+
npmInstall: function npmInstall() {
|
256
|
+
fs.removeSync('../package-lock.json');
|
257
|
+
fs.removeSync('../node_modules/');
|
258
|
+
console.log('Run npm install ...');
|
259
|
+
this.systemSync('npm install');
|
260
|
+
},
|
261
|
+
|
262
|
+
/**
|
263
|
+
* @summary run lint, unit test and integration test
|
264
|
+
* print result to stdout
|
265
|
+
*/
|
266
|
+
runTest: function runTest() {
|
267
|
+
this.systemSync('npm run lint:errors');
|
268
|
+
this.systemSync('npm run test:unit');
|
269
|
+
this.systemSync('npm run test:integration');
|
270
|
+
},
|
271
|
+
|
272
|
+
/**
|
273
|
+
* @summary run basicget with mocha
|
274
|
+
* @param {boolean} scriptFlag - whether the function is ran from a script
|
275
|
+
* print result to stdout
|
276
|
+
* returns mocha test results otherwise
|
277
|
+
*/
|
278
|
+
runBasicGet: function runBasicGet(scriptFlag) {
|
279
|
+
const testPath = path.resolve(__dirname, '..', 'test/integration/adapterTestBasicGet.js');
|
280
|
+
return this.systemSync(`mocha ${testPath} --exit`, !scriptFlag);
|
281
|
+
},
|
282
|
+
|
283
|
+
/**
|
284
|
+
* @summary run connectivity with mocha
|
285
|
+
* @param {String} host - Host url to run healthcheck
|
286
|
+
* @param {boolean} scriptFlag - Whether the function is ran from a script
|
287
|
+
* print result to stdout if ran from script
|
288
|
+
* returns mocha test results otherwise
|
289
|
+
*/
|
290
|
+
runConnectivity: function runConnectivity(host, scriptFlag) {
|
291
|
+
let testPath = 'test/integration/adapterTestConnectivity.js';
|
292
|
+
if (!scriptFlag) {
|
293
|
+
testPath = path.resolve(__dirname, '..', testPath);
|
294
|
+
}
|
295
|
+
return this.systemSync(`mocha ${testPath} --HOST=${host} --timeout 10000 --exit`, !scriptFlag);
|
296
|
+
},
|
297
|
+
|
298
|
+
/**
|
299
|
+
* @summary create Adapter property
|
300
|
+
*
|
301
|
+
* @function createAdapter
|
302
|
+
* @param {Object} pronghornProps - decrypted 'properties.json' from IAP root directory
|
303
|
+
* @param {Object} profileItem - pronghorn props saved in database
|
304
|
+
* @param {Object} adapterPronghorn - ./pronghorn.json in adapter dir
|
305
|
+
* @param {Object} sampleProperties - './sampleProperties.json' in adapter dir
|
306
|
+
*/
|
307
|
+
createAdapter: function createAdapter(pronghornProps, profileItem, sampleProperties, adapterPronghorn) {
|
308
|
+
const dirname = this.getDirname();
|
309
|
+
const packagePath = `${dirname.split('node_modules')[0]}package.json`;
|
310
|
+
const info = JSON.parse(fs.readFileSync(packagePath));
|
311
|
+
const version = parseInt(info.version.split('.')[0], 10);
|
312
|
+
|
313
|
+
let adapter = {};
|
314
|
+
if (version >= 2020) {
|
315
|
+
adapter = {
|
316
|
+
isEncrypted: pronghornProps.pathProps.encrypted,
|
317
|
+
model: adapterPronghorn.id,
|
318
|
+
name: sampleProperties.id,
|
319
|
+
type: adapterPronghorn.type,
|
320
|
+
properties: sampleProperties,
|
321
|
+
loggerProps: profileItem.loggerProps
|
322
|
+
};
|
323
|
+
} else {
|
324
|
+
adapter = {
|
325
|
+
mongoProps: pronghornProps.mongoProps,
|
326
|
+
isEncrypted: pronghornProps.pathProps.encrypted,
|
327
|
+
model: adapterPronghorn.id,
|
328
|
+
name: sampleProperties.id,
|
329
|
+
type: adapterPronghorn.type,
|
330
|
+
properties: sampleProperties,
|
331
|
+
redisProps: profileItem.redisProps,
|
332
|
+
loggerProps: profileItem.loggerProps,
|
333
|
+
rabbitmq: profileItem.rabbitmq
|
334
|
+
};
|
335
|
+
adapter.mongoProps.pdb = true;
|
336
|
+
}
|
337
|
+
|
338
|
+
adapter.loggerProps.log_filename = `adapter-${adapter.name}.log`;
|
339
|
+
return adapter;
|
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, iapDir, 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 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
|
+
}
|
362
|
+
const pronghornProps = this.getPronghornProps(iapDir);
|
363
|
+
console.log('Connecting to Database...');
|
364
|
+
const database = await this.connect(iapDir, pronghornProps);
|
365
|
+
console.log('Connection established.');
|
366
|
+
const { name } = require(path.join(__dirname, '..', 'package.json'));
|
367
|
+
const serviceItem = await database.collection(this.SERVICE_CONFIGS_COLLECTION).findOne(
|
368
|
+
{ model: name }
|
369
|
+
);
|
370
|
+
return { database, serviceItem, pronghornProps };
|
371
|
+
},
|
372
|
+
|
373
|
+
/**
|
374
|
+
* @summary return async healthcheck result as a Promise
|
375
|
+
*
|
376
|
+
* @function request
|
377
|
+
* @param {Adapter} a - Adapter instance
|
378
|
+
*/
|
379
|
+
request: function request(a) {
|
380
|
+
return new Promise((resolve, reject) => {
|
381
|
+
a.healthCheck(null, (data) => {
|
382
|
+
if (!data) reject(new Error('healthCheckEndpoint failed'));
|
383
|
+
resolve(data);
|
384
|
+
});
|
385
|
+
});
|
386
|
+
},
|
387
|
+
|
388
|
+
/**
|
389
|
+
* @summary deal with healthcheck response returned from adapter instace
|
390
|
+
*
|
391
|
+
* @function healthCheck
|
392
|
+
* @param {Adapter} a - Adapter instance
|
393
|
+
*/
|
394
|
+
healthCheck: async function healthCheck(a) {
|
395
|
+
const result = await this.request(a)
|
396
|
+
.then((res) => {
|
397
|
+
console.log('healthCheckEndpoint OK');
|
398
|
+
return res;
|
399
|
+
})
|
400
|
+
.catch((error) => {
|
401
|
+
console.error(error.message);
|
402
|
+
return false;
|
403
|
+
});
|
404
|
+
return result;
|
405
|
+
},
|
406
|
+
|
407
|
+
/**
|
408
|
+
* @summary Check whether adapter is located within IAP node_modules
|
409
|
+
* by loading properties.json. If not, return false.
|
410
|
+
* @function withinIAP
|
411
|
+
* @param {String} iapDir root directory of IAP
|
412
|
+
*/
|
413
|
+
withinIAP: (iapDir) => {
|
414
|
+
try {
|
415
|
+
const rawProps = require(path.join(iapDir, 'properties.json'));
|
416
|
+
return rawProps;
|
417
|
+
} catch (error) {
|
418
|
+
return false;
|
419
|
+
}
|
420
|
+
},
|
421
|
+
|
422
|
+
/**
|
423
|
+
* @summary Used to determine the proper dirname to return in case adapter reference is
|
424
|
+
* symlinked withink IAP
|
425
|
+
* @returns the symlinked path (using pwd command) of the adapter in case properties.json
|
426
|
+
* is not found in the original path
|
427
|
+
* @function getDirname
|
428
|
+
*/
|
429
|
+
getDirname: function getDirname() {
|
430
|
+
if (this.withinIAP(path.join(__dirname, '../../../../'))) {
|
431
|
+
return __dirname;
|
432
|
+
}
|
433
|
+
const { stdout } = this.systemSync('pwd', true);
|
434
|
+
return stdout.trim();
|
435
|
+
},
|
436
|
+
|
437
|
+
/**
|
438
|
+
* @summary connect to mongodb
|
439
|
+
*
|
440
|
+
* @function connect
|
441
|
+
* @param {Object} properties - pronghornProps
|
442
|
+
*/
|
443
|
+
connect: async function connect(iapDir, properties) {
|
444
|
+
// Connect to Mongo
|
445
|
+
const { MongoDBConnection } = require(path.join(iapDir, 'node_modules/@itential/database'));
|
446
|
+
const connection = new MongoDBConnection(properties.mongoProps);
|
447
|
+
const database = await connection.connect(true);
|
448
|
+
return database;
|
449
|
+
}
|
450
|
+
|
451
|
+
};
|
@@ -0,0 +1,190 @@
|
|
1
|
+
/* @copyright Itential, LLC 2020 */
|
2
|
+
/* eslint global-require: warn */
|
3
|
+
/* eslint no-console: warn */
|
4
|
+
/* eslint import/no-unresolved: warn */
|
5
|
+
/* eslint import/no-dynamic-require: warn */
|
6
|
+
|
7
|
+
const path = require('path');
|
8
|
+
const rls = require('readline-sync');
|
9
|
+
const fs = require('fs-extra');
|
10
|
+
|
11
|
+
const utils = require(path.join(__dirname, 'tbUtils'));
|
12
|
+
const basicGet = require(path.join(__dirname, 'basicGet'));
|
13
|
+
const { name } = require(path.join(__dirname, '..', 'package.json'));
|
14
|
+
const sampleProperties = require(path.join(__dirname, '..', 'sampleProperties.json'));
|
15
|
+
|
16
|
+
// send interactive questions and collection answers
|
17
|
+
// return updated connection object
|
18
|
+
const collectAnswersSync = (questions, props) => {
|
19
|
+
const answers = [];
|
20
|
+
questions.forEach((q) => {
|
21
|
+
const answer = rls.question(q);
|
22
|
+
answers.push(answer);
|
23
|
+
});
|
24
|
+
return utils.getNewProps(answers, props);
|
25
|
+
};
|
26
|
+
|
27
|
+
// change object into array of questions
|
28
|
+
const confirm = (props) => {
|
29
|
+
const questions = Object.keys(props).map((key) => `${key}: (${props[key]}) `);
|
30
|
+
return collectAnswersSync(questions, props);
|
31
|
+
};
|
32
|
+
|
33
|
+
// allow user to change auth_method
|
34
|
+
const confirmAuthOptions = (authentication) => {
|
35
|
+
const authOptions = ['basic user_password', 'request_token', 'static_token', 'no_authentication'];
|
36
|
+
const displayAuthOptions = utils.getDisplayAuthOptions(authentication.auth_method, authOptions);
|
37
|
+
const index = rls.keyInSelect(displayAuthOptions, 'Which authentication?');
|
38
|
+
if (index === -1) {
|
39
|
+
return authentication.auth_method;
|
40
|
+
}
|
41
|
+
console.log(`${authOptions[index]} is selected.`);
|
42
|
+
return authOptions[index];
|
43
|
+
};
|
44
|
+
|
45
|
+
// helper function to update auth properties
|
46
|
+
const confirmAndUpdate = (auth, config) => {
|
47
|
+
const newAuth = confirm(auth);
|
48
|
+
return utils.updateAuth(newAuth, auth, config);
|
49
|
+
};
|
50
|
+
|
51
|
+
// extract basic auth properties
|
52
|
+
const updateBasicAuth = (config, authentication) => {
|
53
|
+
const auth = {
|
54
|
+
username: authentication.username,
|
55
|
+
password: authentication.password
|
56
|
+
};
|
57
|
+
return confirmAndUpdate(auth, config);
|
58
|
+
};
|
59
|
+
|
60
|
+
// extract static auth properties
|
61
|
+
const updateStaticAuth = (config, authentication) => {
|
62
|
+
const auth = {
|
63
|
+
token: authentication.token,
|
64
|
+
auth_field: authentication.auth_field,
|
65
|
+
auth_field_format: authentication.auth_field_format
|
66
|
+
};
|
67
|
+
return confirmAndUpdate(auth, config);
|
68
|
+
};
|
69
|
+
|
70
|
+
// troubleshooting connection and healthcheck endpoint setting of adapter
|
71
|
+
const VerifyHealthCheckEndpoint = (serviceItem, props, scriptFlag) => {
|
72
|
+
// Updates connectivity params and runs connectivity
|
73
|
+
let connConfig;
|
74
|
+
const result = {};
|
75
|
+
if (scriptFlag) {
|
76
|
+
const connection = utils.getConnection(serviceItem.properties);
|
77
|
+
const newConnection = confirm(connection);
|
78
|
+
utils.runConnectivity(newConnection.host, scriptFlag);
|
79
|
+
connConfig = utils.updateNewConnection(serviceItem, newConnection);
|
80
|
+
} else {
|
81
|
+
let { properties: { properties: { host } } } = serviceItem;
|
82
|
+
if (props.connProps) {
|
83
|
+
connConfig = utils.updateNewConnection(serviceItem, props.connProps);
|
84
|
+
host = connConfig.properties.properties.host;
|
85
|
+
} else {
|
86
|
+
connConfig = serviceItem;
|
87
|
+
}
|
88
|
+
result.connectivity = utils.runConnectivity(host, scriptFlag);
|
89
|
+
}
|
90
|
+
// Updates the healthcheck endpoing
|
91
|
+
const healthcheck = require('../entities/.system/action.json');
|
92
|
+
const healthCheckEndpoint = utils.getHealthCheckEndpoint(healthcheck);
|
93
|
+
let newHealthCheckEndpoint = healthCheckEndpoint;
|
94
|
+
if (scriptFlag) {
|
95
|
+
newHealthCheckEndpoint = confirm(healthCheckEndpoint);
|
96
|
+
utils.getHealthCheckEndpointURL(newHealthCheckEndpoint, connConfig);
|
97
|
+
} else if (props.healthCheckEndpoint) {
|
98
|
+
newHealthCheckEndpoint = props.healthCheckEndpoint;
|
99
|
+
}
|
100
|
+
// Updates the authorization params
|
101
|
+
const { authentication } = connConfig.properties.properties;
|
102
|
+
let updatedAdapter = connConfig;
|
103
|
+
if (scriptFlag) {
|
104
|
+
authentication.auth_method = confirmAuthOptions(authentication);
|
105
|
+
if (authentication.auth_method === 'basic user_password') {
|
106
|
+
updatedAdapter = updateBasicAuth(connConfig, authentication);
|
107
|
+
} else if (authentication.auth_method === 'static_token') {
|
108
|
+
updatedAdapter = updateStaticAuth(connConfig, authentication);
|
109
|
+
} else if (authentication.auth_method === 'request_token') {
|
110
|
+
console.log('current troubleshooting script does not support updating request_token authentication');
|
111
|
+
}
|
112
|
+
} else if (props.auth) {
|
113
|
+
updatedAdapter = utils.updateAuth(props.auth, authentication, connConfig);
|
114
|
+
}
|
115
|
+
// Writes the new healthcheck endpoint into action.json
|
116
|
+
utils.updateHealthCheckEndpoint(newHealthCheckEndpoint, healthCheckEndpoint, healthcheck);
|
117
|
+
return { result, updatedAdapter };
|
118
|
+
};
|
119
|
+
|
120
|
+
const offline = async () => {
|
121
|
+
console.log('Start offline troubleshooting');
|
122
|
+
const { updatedAdapter } = VerifyHealthCheckEndpoint({ properties: sampleProperties }, {}, true);
|
123
|
+
const a = basicGet.getAdapterInstance(updatedAdapter);
|
124
|
+
const res = await utils.healthCheck(a);
|
125
|
+
if (!res) {
|
126
|
+
console.log('run `npm run troubleshoot` again to update settings');
|
127
|
+
process.exit(0);
|
128
|
+
}
|
129
|
+
console.log('Save changes to sampleProperties.json');
|
130
|
+
await fs.writeFile('sampleProperties.json', JSON.stringify(updatedAdapter.properties, null, 2));
|
131
|
+
if (rls.keyInYN('Test with more GET request')) {
|
132
|
+
await utils.runBasicGet(true);
|
133
|
+
}
|
134
|
+
};
|
135
|
+
|
136
|
+
const troubleshoot = async (props, scriptFlag, persistFlag, adapter) => {
|
137
|
+
// get database connection and existing adapter config
|
138
|
+
const { database, serviceItem } = await utils.getAdapterConfig();
|
139
|
+
// where troubleshoot should start
|
140
|
+
if (serviceItem) {
|
141
|
+
if (!scriptFlag || rls.keyInYN(`Start verifying the connection and authentication properties for ${name}?`)) {
|
142
|
+
const { result, updatedAdapter } = VerifyHealthCheckEndpoint(serviceItem, props, scriptFlag);
|
143
|
+
let a;
|
144
|
+
if (scriptFlag) {
|
145
|
+
a = basicGet.getAdapterInstance(updatedAdapter);
|
146
|
+
} else {
|
147
|
+
a = adapter;
|
148
|
+
}
|
149
|
+
const healthRes = await utils.healthCheck(a);
|
150
|
+
result.healthCheck = healthRes;
|
151
|
+
if (scriptFlag && !healthRes) {
|
152
|
+
console.log('run `npm run troubleshoot` again to update settings');
|
153
|
+
process.exit(0);
|
154
|
+
}
|
155
|
+
|
156
|
+
if (persistFlag && healthRes) {
|
157
|
+
const update = { $set: { properties: updatedAdapter.properties } };
|
158
|
+
await database.collection(utils.SERVICE_CONFIGS_COLLECTION).updateOne(
|
159
|
+
{ model: name }, update
|
160
|
+
);
|
161
|
+
if (scriptFlag) {
|
162
|
+
console.log(`${name} updated.`);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
if (scriptFlag) {
|
166
|
+
if (rls.keyInYN('Test with more GET request')) {
|
167
|
+
await utils.runBasicGet(scriptFlag);
|
168
|
+
process.exit(0);
|
169
|
+
} else {
|
170
|
+
console.log('Exiting');
|
171
|
+
process.exit(0);
|
172
|
+
}
|
173
|
+
} else {
|
174
|
+
result.basicGet = await utils.runBasicGet(scriptFlag);
|
175
|
+
return result;
|
176
|
+
}
|
177
|
+
} else {
|
178
|
+
console.log('You can update healthCheckEndpoint in ./entities/.system/action.json');
|
179
|
+
console.log('You can update authentication credientials under Settings/Services');
|
180
|
+
console.log('Exiting');
|
181
|
+
process.exit(0);
|
182
|
+
}
|
183
|
+
} else {
|
184
|
+
console.log(`${name} not installed`);
|
185
|
+
console.log('run `npm run install:adapter` to install current adapter to IAP first. Exiting...');
|
186
|
+
}
|
187
|
+
return null;
|
188
|
+
};
|
189
|
+
|
190
|
+
module.exports = { troubleshoot, offline };
|