@itentialopensource/adapter-utils 5.10.15 → 5.10.17
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/README.md +1 -1
- package/lib/connectorRest.js +22 -160
- package/lib/constants.js +14 -0
- package/lib/propertyUtil.js +24 -9
- package/lib/restHandler.js +39 -120
- package/lib/translatorUtil.js +177 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ Itential Adapter Utilities
|
|
|
3
3
|
|
|
4
4
|
Itential Adapter Utilities is a set of Node.js runtime libraries used by Itential Adapters.
|
|
5
5
|
|
|
6
|
-
Many of the capabilities supported in Itential Adapters are built into this library so that they can
|
|
6
|
+
Many of the capabilities supported in Itential Adapters are built into this library so that they can be better maintained and added to adapters.
|
|
7
7
|
|
|
8
8
|
# Usage
|
|
9
9
|
|
package/lib/connectorRest.js
CHANGED
|
@@ -267,54 +267,6 @@ function waitForSystem(callProperties) {
|
|
|
267
267
|
});
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
/*
|
|
271
|
-
* INTERNAL FUNCTION: Get the best match for the moc k data response
|
|
272
|
-
*/
|
|
273
|
-
function matchMock(uriPath, method, type, mockresponses, respDatatype) {
|
|
274
|
-
// Go through the mock data keys to find the proper data to return
|
|
275
|
-
for (let p = 0; p < mockresponses.length; p += 1) {
|
|
276
|
-
// is this the mock data for this call
|
|
277
|
-
if (Object.hasOwnProperty.call(mockresponses[p], 'name')
|
|
278
|
-
&& uriPath === mockresponses[p].name) {
|
|
279
|
-
if (Object.hasOwnProperty.call(mockresponses[p], 'method')
|
|
280
|
-
&& method.toUpperCase() === mockresponses[p].method.toUpperCase()) {
|
|
281
|
-
if (Object.hasOwnProperty.call(mockresponses[p], 'type')
|
|
282
|
-
&& type.toUpperCase() === mockresponses[p].type.toUpperCase()) {
|
|
283
|
-
// This is the mock data we really want as it best matches the request
|
|
284
|
-
const specificResp = {
|
|
285
|
-
status: 'success',
|
|
286
|
-
code: 200,
|
|
287
|
-
response: {}
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
if (Object.hasOwnProperty.call(mockresponses[p], 'response')) {
|
|
291
|
-
// if a status is defined in the response, use it
|
|
292
|
-
if (Object.hasOwnProperty.call(mockresponses[p].response, 'response')
|
|
293
|
-
&& Object.hasOwnProperty.call(mockresponses[p].response, 'status')) {
|
|
294
|
-
specificResp.code = mockresponses[p].response.status;
|
|
295
|
-
|
|
296
|
-
if (respDatatype && respDatatype.toUpperCase() === 'XML2JSON') {
|
|
297
|
-
specificResp.response = mockresponses[p].response.response;
|
|
298
|
-
} else {
|
|
299
|
-
specificResp.response = JSON.stringify(mockresponses[p].response.response);
|
|
300
|
-
}
|
|
301
|
-
} else if (respDatatype && respDatatype.toUpperCase() === 'XML2JSON') {
|
|
302
|
-
// if no response field, assume that the entire data is the response
|
|
303
|
-
specificResp.response = mockresponses[p].response;
|
|
304
|
-
} else {
|
|
305
|
-
specificResp.response = JSON.stringify(mockresponses[p].response);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return specificResp;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
270
|
/*
|
|
319
271
|
* INTERNAL FUNCTION: recursively inspect body data if heirarchical
|
|
320
272
|
*/
|
|
@@ -347,20 +299,20 @@ function checkBodyData(uriPath, method, reqBdObj, mockresponses, respDatatype) {
|
|
|
347
299
|
for (let a = 0; a < bVal.length; a += 1) {
|
|
348
300
|
// should match fieldName-fieldValue
|
|
349
301
|
const compStr = `${reqBKeys[k]}-${bVal[a]}`;
|
|
350
|
-
specificResp =
|
|
302
|
+
specificResp = transUtilInst.matchResponse(uriPath, method, compStr, mockresponses, respDatatype);
|
|
351
303
|
|
|
352
304
|
// if the data match is found break the for loop - will return below
|
|
353
|
-
if (specificResp !== null) {
|
|
305
|
+
if (specificResp !== null && specificResp.response) {
|
|
354
306
|
break;
|
|
355
307
|
}
|
|
356
308
|
}
|
|
357
309
|
} else {
|
|
358
310
|
// should match fieldName-fieldValue
|
|
359
311
|
const compStr = `${reqBKeys[k]}-${bVal}`;
|
|
360
|
-
specificResp =
|
|
312
|
+
specificResp = transUtilInst.matchResponse(uriPath, method, compStr, mockresponses, respDatatype);
|
|
361
313
|
}
|
|
362
314
|
|
|
363
|
-
if (specificResp !== null) {
|
|
315
|
+
if (specificResp !== null && specificResp.response) {
|
|
364
316
|
break;
|
|
365
317
|
}
|
|
366
318
|
}
|
|
@@ -441,56 +393,9 @@ function returnStub(request, entitySchema, callProperties) {
|
|
|
441
393
|
}
|
|
442
394
|
|
|
443
395
|
// if there are path variables, see if there is something that matches a specific variable
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
uriTemp[0] = uriTemp[0].replace(/{pathv/g, '/{pathv');
|
|
448
|
-
uriTemp[0] = uriTemp[0].replace(/{version/g, '/{version');
|
|
449
|
-
uriTemp[0] = uriTemp[0].replace(/{base/g, '/{base');
|
|
450
|
-
uriTemp[0] = uriTemp[0].replace(/\/\//g, '/');
|
|
451
|
-
|
|
452
|
-
// remove basepath from both paths
|
|
453
|
-
// get rid of base path from the uriPath
|
|
454
|
-
uriTemp[0] = uriTemp[0].replace(/\/{base_path}/g, '');
|
|
455
|
-
// if a base path was added to the request, remove it
|
|
456
|
-
if (callProperties && callProperties.base_path && callProperties.base_path !== '/') {
|
|
457
|
-
actTemp[0] = actTemp[0].replace(callProperties.base_path, '');
|
|
458
|
-
} else if (basepath && basepath !== '/') {
|
|
459
|
-
actTemp[0] = actTemp[0].replace(basepath, '');
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// remove version from both paths
|
|
463
|
-
// get rid of version from the uriPath
|
|
464
|
-
uriTemp[0] = uriTemp[0].replace(/\/{version}/g, '');
|
|
465
|
-
// if a version was added to the request, remove it
|
|
466
|
-
if (callProperties && callProperties.version) {
|
|
467
|
-
actTemp[0] = actTemp[0].replace(`/${callProperties.version}`, '');
|
|
468
|
-
} else if (version && version !== '/') {
|
|
469
|
-
actTemp[0] = actTemp[0].replace(`/${version}`, '');
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
const uriArray = uriTemp[0].split('/');
|
|
473
|
-
const actArray = actTemp[0].split('/');
|
|
474
|
-
|
|
475
|
-
// if this is one of the generic requests need to pad the actual array or mock data for specific path variable will not work
|
|
476
|
-
if (entitySchema.entitypath.indexOf('/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}/{pathv11}/{pathv12}/{pathv13}/{pathv14}/{pathv15}/{pathv16}/{pathv17}/{pathv18}/{pathv19}/{pathv20}?{query}') >= 0) {
|
|
477
|
-
for (let e = actArray.length; e < uriArray.length; e += 1) {
|
|
478
|
-
actArray.push('');
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// the number of items in both should be the same
|
|
483
|
-
if (uriArray.length === actArray.length) {
|
|
484
|
-
for (let i = 0; i < uriArray.length; i += 1) {
|
|
485
|
-
if (uriArray[i].indexOf('{pathv') >= 0) {
|
|
486
|
-
specificResp = matchMock(uriPath, method, actArray[i], mockresponses, entitySchema.responseDatatype);
|
|
487
|
-
|
|
488
|
-
if (specificResp !== null) {
|
|
489
|
-
return specificResp;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
396
|
+
specificResp = transUtilInst.getRespObjKeyFromPath(uriPath, reqPath, callProperties, basepath, version, method, false, entitySchema);
|
|
397
|
+
if (specificResp !== null && specificResp.response) {
|
|
398
|
+
return specificResp;
|
|
494
399
|
}
|
|
495
400
|
|
|
496
401
|
// if there are queiries or options, see if there is something that matches a specific input
|
|
@@ -504,9 +409,9 @@ function returnStub(request, entitySchema, callProperties) {
|
|
|
504
409
|
if (qval !== undefined && qval !== null && qval !== '') {
|
|
505
410
|
// stringifies it - in case it was not a string
|
|
506
411
|
qval = `${qval}`;
|
|
507
|
-
specificResp =
|
|
412
|
+
specificResp = transUtilInst.matchResponse(uriPath, method, qval, mockresponses, entitySchema.responseDatatype);
|
|
508
413
|
|
|
509
|
-
if (specificResp !== null) {
|
|
414
|
+
if (specificResp !== null && specificResp.response) {
|
|
510
415
|
return specificResp;
|
|
511
416
|
}
|
|
512
417
|
}
|
|
@@ -515,79 +420,36 @@ function returnStub(request, entitySchema, callProperties) {
|
|
|
515
420
|
|
|
516
421
|
// if there is a request body, see if there is a specific response for body
|
|
517
422
|
if (reqBody) {
|
|
518
|
-
specificResp =
|
|
519
|
-
|
|
520
|
-
if (specificResp !== null) {
|
|
423
|
+
specificResp = transUtilInst.matchResponse(uriPath, method, 'WITHBODY', mockresponses, entitySchema.responseDatatype);
|
|
424
|
+
if (specificResp !== null && specificResp.response) {
|
|
521
425
|
return specificResp;
|
|
522
426
|
}
|
|
523
427
|
}
|
|
524
428
|
|
|
525
|
-
// if there are path variables, see if there is
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
uriTemp[0] = uriTemp[0].replace(/{pathv/g, '/{pathv');
|
|
530
|
-
uriTemp[0] = uriTemp[0].replace(/{version/g, '/{version');
|
|
531
|
-
uriTemp[0] = uriTemp[0].replace(/{base_path}/g, '/{base_path}');
|
|
532
|
-
uriTemp[0] = uriTemp[0].replace(/\/\//g, '/');
|
|
533
|
-
|
|
534
|
-
// remove basepath from both paths
|
|
535
|
-
// get rid of base path from the uriPath
|
|
536
|
-
uriTemp[0] = uriTemp[0].replace(/\/{base_path}/g, '');
|
|
537
|
-
// if a base path was added to the request, remove it
|
|
538
|
-
if (callProperties && callProperties.base_path && callProperties.base_path !== '/') {
|
|
539
|
-
actTemp[0] = actTemp[0].replace(callProperties.base_path, '');
|
|
540
|
-
} else if (basepath && basepath !== '/') {
|
|
541
|
-
actTemp[0] = actTemp[0].replace(basepath, '');
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// remove version from both paths
|
|
545
|
-
// get rid of version from the uriPath
|
|
546
|
-
uriTemp[0] = uriTemp[0].replace(/\/{version}/g, '');
|
|
547
|
-
// if a version was added to the request, remove it
|
|
548
|
-
if (callProperties && callProperties.version) {
|
|
549
|
-
actTemp[0] = actTemp[0].replace(`/${callProperties.version}`, '');
|
|
550
|
-
} else if (version && version !== '/') {
|
|
551
|
-
actTemp[0] = actTemp[0].replace(`/${version}`, '');
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
const uriArray = uriTemp[0].split('/');
|
|
555
|
-
const actArray = actTemp[0].split('/');
|
|
556
|
-
|
|
557
|
-
// the number of items in both should be the same
|
|
558
|
-
if (uriArray.length === actArray.length) {
|
|
559
|
-
let cnt = 1;
|
|
560
|
-
for (let i = 0; i < uriArray.length; i += 1) {
|
|
561
|
-
if (uriArray[i].indexOf('{pathv') >= 0) {
|
|
562
|
-
specificResp = matchMock(uriPath, method, `WITHPATHV${cnt}`, mockresponses, entitySchema.responseDatatype);
|
|
563
|
-
|
|
564
|
-
if (specificResp !== null) {
|
|
565
|
-
return specificResp;
|
|
566
|
-
}
|
|
567
|
-
cnt += 1;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
}
|
|
429
|
+
// if there are path variables, see if there is something that matches a specific variable
|
|
430
|
+
specificResp = transUtilInst.getRespObjKeyFromPath(uriPath, reqPath, callProperties, basepath, version, method, true, entitySchema);
|
|
431
|
+
if (specificResp !== null && specificResp.response) {
|
|
432
|
+
return specificResp;
|
|
571
433
|
}
|
|
572
434
|
|
|
573
435
|
// if there are queiries or options, see if there is a specific response for query or options
|
|
574
436
|
if (uriPath.indexOf('?') >= 0) {
|
|
575
|
-
specificResp =
|
|
437
|
+
specificResp = transUtilInst.matchResponse(uriPath, method, 'WITHQUERY', mockresponses, entitySchema.responseDatatype);
|
|
576
438
|
|
|
577
|
-
if (specificResp !== null) {
|
|
439
|
+
if (specificResp !== null && specificResp.response) {
|
|
578
440
|
return specificResp;
|
|
579
441
|
}
|
|
580
442
|
|
|
581
|
-
specificResp =
|
|
443
|
+
specificResp = transUtilInst.matchResponse(uriPath, method, 'WITHOPTIONS', mockresponses, entitySchema.responseDatatype);
|
|
582
444
|
|
|
583
|
-
if (specificResp !== null) {
|
|
445
|
+
if (specificResp !== null && specificResp.response) {
|
|
584
446
|
return specificResp;
|
|
585
447
|
}
|
|
586
448
|
}
|
|
587
449
|
|
|
588
|
-
specificResp =
|
|
450
|
+
specificResp = transUtilInst.matchResponse(uriPath, method, 'DEFAULT', mockresponses, entitySchema.responseDatatype);
|
|
589
451
|
|
|
590
|
-
if (specificResp !== null) {
|
|
452
|
+
if (specificResp !== null && specificResp.response) {
|
|
591
453
|
return specificResp;
|
|
592
454
|
}
|
|
593
455
|
|
|
@@ -4579,7 +4441,7 @@ class ConnectorRest {
|
|
|
4579
4441
|
return waitForMongo()
|
|
4580
4442
|
.then(() => {
|
|
4581
4443
|
if (archiving) {
|
|
4582
|
-
const colRes = this.dbUtil.createCollection(archiveColl, (err, res) => {
|
|
4444
|
+
const colRes = this.dbUtil.createCollection(archiveColl, null, null, (err, res) => {
|
|
4583
4445
|
if (err) {
|
|
4584
4446
|
log.error(`${origin}: collection ${archiveColl} could not be created.`);
|
|
4585
4447
|
return callback(false);
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* @copyright Itential, LLC 2025 */
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
// Global Strings
|
|
5
|
+
globalStrings: {
|
|
6
|
+
pathVarMarker: '{pathv', // path variable format is {pathv#} - number is dynamic can not match
|
|
7
|
+
versionMarker: '{version}',
|
|
8
|
+
basePathMarker: '{base_path}',
|
|
9
|
+
withPathVarPrefix: 'WITHPATHV',
|
|
10
|
+
urlSplit: '?',
|
|
11
|
+
pathSplit: '/',
|
|
12
|
+
globalFlag: 'g'
|
|
13
|
+
}
|
|
14
|
+
};
|
package/lib/propertyUtil.js
CHANGED
|
@@ -550,6 +550,7 @@ class AdapterPropertyUtil {
|
|
|
550
550
|
name: entitySchema.responseObjects[i].name,
|
|
551
551
|
method: entitySchema.responseObjects[i].method,
|
|
552
552
|
type: entitySchema.responseObjects[i].type,
|
|
553
|
+
key: entitySchema.responseObjects[i].key || '',
|
|
553
554
|
file: entitySchema.responseObjects[i].mockFile
|
|
554
555
|
};
|
|
555
556
|
|
|
@@ -969,6 +970,7 @@ class AdapterPropertyUtil {
|
|
|
969
970
|
name: entitySchema.responseObjects[i].name,
|
|
970
971
|
method: entitySchema.responseObjects[i].method,
|
|
971
972
|
type: entitySchema.responseObjects[i].type,
|
|
973
|
+
key: entitySchema.responseObjects[i].key || '',
|
|
972
974
|
file: entitySchema.responseObjects[i].mockFile
|
|
973
975
|
};
|
|
974
976
|
|
|
@@ -1153,7 +1155,7 @@ class AdapterPropertyUtil {
|
|
|
1153
1155
|
}
|
|
1154
1156
|
|
|
1155
1157
|
// This is the array of sensitive keys
|
|
1156
|
-
let sensList = ['authorization', 'x-auth-token', 'x-csrf-token', 'x-amz-security-token', 'x-aws-ec2-metadata-token', 'cookie', 'set-cookie', 'token', 'tokenp2', 'user', 'username', 'passwd', 'password', 'api-key', 'client-id', 'client-secret', 'client_id', 'client_secret', 'session', 'session-id', 'jsessionid', 'sessionToken', 'accessKeyId', 'secretAccessKey', 'private-token', 'ca'];
|
|
1158
|
+
let sensList = ['authorization', 'x-auth-token', 'x-csrf-token', 'x-amz-security-token', 'x-aws-ec2-metadata-token', 'cookie', 'set-cookie', 'token', 'tokenp2', 'user', 'username', 'passwd', 'password', 'api-key', 'client-id', 'client-secret', 'client_id', 'client_secret', 'session', 'session-id', 'jsessionid', 'sessionToken', 'accessKeyId', 'secretAccessKey', 'private-token', 'ca'].map((item) => item.toLowerCase());
|
|
1157
1159
|
|
|
1158
1160
|
// add any additional items to scrub
|
|
1159
1161
|
if (addItems && Array.isArray(addItems) && addItems.length > 0) {
|
|
@@ -1207,7 +1209,7 @@ class AdapterPropertyUtil {
|
|
|
1207
1209
|
|
|
1208
1210
|
// go through sensitive word list - maybe can use find in
|
|
1209
1211
|
for (let j = 0; j < sensList.length; j += 1) {
|
|
1210
|
-
if (key.toLowerCase() === sensList[j]
|
|
1212
|
+
if (key.toLowerCase() === sensList[j]) {
|
|
1211
1213
|
// if sensitive, mask
|
|
1212
1214
|
retData += `${key}=** masked **`;
|
|
1213
1215
|
found = true;
|
|
@@ -1229,12 +1231,19 @@ class AdapterPropertyUtil {
|
|
|
1229
1231
|
|
|
1230
1232
|
// want to make a copy and not alter the original object or array
|
|
1231
1233
|
const retData = JSON.parse(JSON.stringify(actualData));
|
|
1232
|
-
|
|
1234
|
+
if (retData && typeof retData.response === 'string') {
|
|
1235
|
+
try {
|
|
1236
|
+
const parsed = JSON.parse(retData.response);
|
|
1237
|
+
retData.response = parsed;
|
|
1238
|
+
} catch (e) {
|
|
1239
|
+
// ignore parse error
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1233
1242
|
// if we are scrubbing an array
|
|
1234
1243
|
if (Array.isArray(retData)) {
|
|
1235
1244
|
// need to go through each item in the array
|
|
1236
1245
|
for (let i = 0; i < retData.length; i += 1) {
|
|
1237
|
-
retData[i] = this.scrubSensitiveInfo(retData[i]);
|
|
1246
|
+
retData[i] = this.scrubSensitiveInfo(retData[i], addItems);
|
|
1238
1247
|
}
|
|
1239
1248
|
|
|
1240
1249
|
// return the scrubbed array
|
|
@@ -1253,21 +1262,27 @@ class AdapterPropertyUtil {
|
|
|
1253
1262
|
if (sensList.includes(key.toLowerCase())) {
|
|
1254
1263
|
retData[key][k] = '** masked **';
|
|
1255
1264
|
} else {
|
|
1256
|
-
retData[key][k] = this.scrubSensitiveInfo(retData[key][k]);
|
|
1265
|
+
retData[key][k] = this.scrubSensitiveInfo(retData[key][k], addItems);
|
|
1257
1266
|
}
|
|
1258
1267
|
}
|
|
1259
1268
|
} else {
|
|
1260
|
-
retData[key] = this.scrubSensitiveInfo(retData[key]);
|
|
1269
|
+
retData[key] = this.scrubSensitiveInfo(retData[key], addItems);
|
|
1261
1270
|
}
|
|
1262
1271
|
} else {
|
|
1263
|
-
|
|
1272
|
+
let gotit = false;
|
|
1273
|
+
// go through sensitive word list to mask the entire string
|
|
1264
1274
|
for (let j = 0; j < sensList.length; j += 1) {
|
|
1265
|
-
if (key.
|
|
1275
|
+
if (key.toLowerCase() === sensList[j]) {
|
|
1266
1276
|
// if sensitive, mask
|
|
1267
|
-
retData[key] = '
|
|
1277
|
+
retData[key] = '** masked **';
|
|
1278
|
+
gotit = true;
|
|
1268
1279
|
break;
|
|
1269
1280
|
}
|
|
1270
1281
|
}
|
|
1282
|
+
// if not masking the string as a whole - may need to parse part of it
|
|
1283
|
+
if (!gotit) {
|
|
1284
|
+
retData[key] = this.scrubSensitiveInfo(retData[key], addItems);
|
|
1285
|
+
}
|
|
1271
1286
|
}
|
|
1272
1287
|
});
|
|
1273
1288
|
|
package/lib/restHandler.js
CHANGED
|
@@ -28,31 +28,6 @@ let stripEscapes = false;
|
|
|
28
28
|
let returnResponseHeaders = true;
|
|
29
29
|
let healthcheckHeaders = null;
|
|
30
30
|
let xmlArrayKeys = null;
|
|
31
|
-
// INTERNAL FUNCTIONS
|
|
32
|
-
/*
|
|
33
|
-
* INTERNAL FUNCTION: Get the best match for the mock data response
|
|
34
|
-
*/
|
|
35
|
-
function matchResponse(uriPath, method, type, mockresponses) {
|
|
36
|
-
// Go through the mock data keys to find the proper data to return
|
|
37
|
-
for (let p = 0; p < mockresponses.length; p += 1) {
|
|
38
|
-
// is this the mock data for this call
|
|
39
|
-
if (Object.hasOwnProperty.call(mockresponses[p], 'name')
|
|
40
|
-
&& uriPath === mockresponses[p].name) {
|
|
41
|
-
if (Object.hasOwnProperty.call(mockresponses[p], 'method')
|
|
42
|
-
&& method.toUpperCase() === mockresponses[p].method.toUpperCase()) {
|
|
43
|
-
if (Object.hasOwnProperty.call(mockresponses[p], 'type')
|
|
44
|
-
&& type.toUpperCase() === mockresponses[p].type.toUpperCase()) {
|
|
45
|
-
// This is the Key we really want as it best matches the request
|
|
46
|
-
if (Object.hasOwnProperty.call(mockresponses[p], 'key')) {
|
|
47
|
-
return mockresponses[p].key;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
31
|
|
|
57
32
|
/*
|
|
58
33
|
* INTERNAL FUNCTION: recursively inspect body data if heirarchical
|
|
@@ -84,9 +59,12 @@ function checkBodyData(uriPath, method, reqBdObj, mockresponses) {
|
|
|
84
59
|
} else if (Array.isArray(bVal)) {
|
|
85
60
|
// if an array of data, need to check each data in the array
|
|
86
61
|
for (let a = 0; a < bVal.length; a += 1) {
|
|
87
|
-
// should match fieldName-fieldValue
|
|
62
|
+
// should match fieldName-fieldValue - NOTE: if we get the responseDatatype that should be passed in
|
|
88
63
|
const compStr = `${reqBKeys[k]}-${bVal[a]}`;
|
|
89
|
-
|
|
64
|
+
const response = transUtilInst.matchResponse(uriPath, method, compStr, mockresponses, null);
|
|
65
|
+
if (response) {
|
|
66
|
+
specificResp = response.key;
|
|
67
|
+
}
|
|
90
68
|
|
|
91
69
|
// if the data match is found break the for loop - will return below
|
|
92
70
|
if (specificResp !== null) {
|
|
@@ -94,9 +72,12 @@ function checkBodyData(uriPath, method, reqBdObj, mockresponses) {
|
|
|
94
72
|
}
|
|
95
73
|
}
|
|
96
74
|
} else {
|
|
97
|
-
// should match fieldName-fieldValue
|
|
75
|
+
// should match fieldName-fieldValue - NOTE: if we get the responseDatatype that should be passed in
|
|
98
76
|
const compStr = `${reqBKeys[k]}-${bVal}`;
|
|
99
|
-
|
|
77
|
+
const response = transUtilInst.matchResponse(uriPath, method, compStr, mockresponses, null);
|
|
78
|
+
if (response) {
|
|
79
|
+
specificResp = response.key;
|
|
80
|
+
}
|
|
100
81
|
}
|
|
101
82
|
|
|
102
83
|
if (specificResp !== null) {
|
|
@@ -271,48 +252,10 @@ function handleRestRequest(request, entityId, entitySchema, callProperties, filt
|
|
|
271
252
|
}
|
|
272
253
|
|
|
273
254
|
// if there are path variables, see if there is something that matches a specific variable
|
|
274
|
-
if (respObjKey === null
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
uriTemp[0] = uriTemp[0].replace(/{version/g, '/{version');
|
|
279
|
-
uriTemp[0] = uriTemp[0].replace(/{base/g, '/{base');
|
|
280
|
-
uriTemp[0] = uriTemp[0].replace(/\/\//g, '/');
|
|
281
|
-
|
|
282
|
-
// remove basepath from both paths
|
|
283
|
-
// get rid of base path from the uriPath
|
|
284
|
-
uriTemp[0] = uriTemp[0].replace(/\/{base_path}/g, '');
|
|
285
|
-
// if a base path was added to the request, remove it
|
|
286
|
-
if (callProperties && callProperties.base_path && callProperties.base_path !== '/') {
|
|
287
|
-
actTemp[0] = actTemp[0].replace(callProperties.base_path, '');
|
|
288
|
-
} else if (basepathGl && basepathGl !== '/') {
|
|
289
|
-
actTemp[0] = actTemp[0].replace(basepathGl, '');
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// remove version from both paths
|
|
293
|
-
// get rid of version from the uriPath
|
|
294
|
-
uriTemp[0] = uriTemp[0].replace(/\/{version}/g, '');
|
|
295
|
-
// if a version was added to the request, remove it
|
|
296
|
-
if (callProperties && callProperties.version) {
|
|
297
|
-
actTemp[0] = actTemp[0].replace(`/${callProperties.version}`, '');
|
|
298
|
-
} else if (versionGl && versionGl !== '/') {
|
|
299
|
-
actTemp[0] = actTemp[0].replace(`/${versionGl}`, '');
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const uriArray = uriTemp[0].split('/');
|
|
303
|
-
const actArray = actTemp[0].split('/');
|
|
304
|
-
|
|
305
|
-
// the number of items in both should be the same
|
|
306
|
-
if (uriArray.length === actArray.length) {
|
|
307
|
-
for (let i = 0; i < uriArray.length; i += 1) {
|
|
308
|
-
if (uriArray[i].indexOf('{pathv') >= 0) {
|
|
309
|
-
respObjKey = matchResponse(uriPath, method, actArray[i], responseKeys);
|
|
310
|
-
|
|
311
|
-
if (respObjKey !== null) {
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
255
|
+
if (respObjKey === null) {
|
|
256
|
+
const specificResp = transUtilInst.getRespObjKeyFromPath(uriPath, reqPath, callProperties, basepathGl, versionGl, method, false, entitySchema);
|
|
257
|
+
if (specificResp) {
|
|
258
|
+
respObjKey = specificResp.key;
|
|
316
259
|
}
|
|
317
260
|
}
|
|
318
261
|
|
|
@@ -326,7 +269,10 @@ function handleRestRequest(request, entityId, entitySchema, callProperties, filt
|
|
|
326
269
|
if (qval !== undefined && qval !== null && qval !== '') {
|
|
327
270
|
// stringifies it - in case it was not a string
|
|
328
271
|
qval = `${qval}`;
|
|
329
|
-
|
|
272
|
+
const specificResp = transUtilInst.matchResponse(uriPath, method, qval, responseKeys, entitySchema.responseDatatype);
|
|
273
|
+
if (specificResp) {
|
|
274
|
+
respObjKey = specificResp.key;
|
|
275
|
+
}
|
|
330
276
|
|
|
331
277
|
if (respObjKey !== null) {
|
|
332
278
|
break;
|
|
@@ -337,68 +283,40 @@ function handleRestRequest(request, entityId, entitySchema, callProperties, filt
|
|
|
337
283
|
|
|
338
284
|
// if there is a request body, see if there is a specific response for body
|
|
339
285
|
if (respObjKey === null && reqBody) {
|
|
340
|
-
|
|
286
|
+
const specificResp = transUtilInst.matchResponse(uriPath, method, 'WITHBODY', responseKeys, entitySchema.responseDatatype);
|
|
287
|
+
if (specificResp) {
|
|
288
|
+
respObjKey = specificResp.key;
|
|
289
|
+
}
|
|
341
290
|
}
|
|
342
291
|
|
|
343
292
|
// if there are path variables, see if there is a specific response for path vars
|
|
344
|
-
if (respObjKey === null
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
uriTemp[0] = uriTemp[0].replace(/{version/g, '/{version');
|
|
349
|
-
uriTemp[0] = uriTemp[0].replace(/{base/g, '/{base');
|
|
350
|
-
uriTemp[0] = uriTemp[0].replace(/\/\//g, '/');
|
|
351
|
-
|
|
352
|
-
// remove basepath from both paths
|
|
353
|
-
// get rid of base path from the uriPath
|
|
354
|
-
uriTemp[0] = uriTemp[0].replace(/\/{base_path}/g, '');
|
|
355
|
-
// if a base path was added to the request, remove it
|
|
356
|
-
if (callProperties && callProperties.base_path && callProperties.base_path !== '/') {
|
|
357
|
-
actTemp[0] = actTemp[0].replace(callProperties.base_path, '');
|
|
358
|
-
} else if (basepathGl && basepathGl !== '/') {
|
|
359
|
-
actTemp[0] = actTemp[0].replace(basepathGl, '');
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// remove version from both paths
|
|
363
|
-
// get rid of version from the uriPath
|
|
364
|
-
uriTemp[0] = uriTemp[0].replace(/\/{version}/g, '');
|
|
365
|
-
// if a version was added to the request, remove it
|
|
366
|
-
if (callProperties && callProperties.version) {
|
|
367
|
-
actTemp[0] = actTemp[0].replace(`/${callProperties.version}`, '');
|
|
368
|
-
} else if (versionGl && versionGl !== '/') {
|
|
369
|
-
actTemp[0] = actTemp[0].replace(`/${versionGl}`, '');
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const uriArray = uriTemp[0].split('/');
|
|
373
|
-
const actArray = actTemp[0].split('/');
|
|
374
|
-
|
|
375
|
-
// the number of items in both should be the same
|
|
376
|
-
if (uriArray.length === actArray.length) {
|
|
377
|
-
let cnt = 1;
|
|
378
|
-
for (let i = 0; i < uriArray.length; i += 1) {
|
|
379
|
-
if (uriArray[i].indexOf('{pathv') >= 0) {
|
|
380
|
-
respObjKey = matchResponse(uriPath, method, `WITHPATHV${cnt}`, responseKeys);
|
|
381
|
-
|
|
382
|
-
if (respObjKey !== null) {
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
cnt += 1;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
293
|
+
if (respObjKey === null) {
|
|
294
|
+
const specificResp = transUtilInst.getRespObjKeyFromPath(uriPath, reqPath, callProperties, basepathGl, versionGl, method, true, entitySchema);
|
|
295
|
+
if (specificResp) {
|
|
296
|
+
respObjKey = specificResp.key;
|
|
388
297
|
}
|
|
389
298
|
}
|
|
390
299
|
|
|
391
300
|
// if there are queiries or options, see if there is a specific response for query or options
|
|
392
301
|
if (respObjKey === null && uriPath.indexOf('?') >= 0) {
|
|
393
|
-
|
|
302
|
+
let specificResp = transUtilInst.matchResponse(uriPath, method, 'WITHQUERY', responseKeys, entitySchema.responseDatatype);
|
|
303
|
+
if (specificResp) {
|
|
304
|
+
respObjKey = specificResp.key;
|
|
305
|
+
}
|
|
394
306
|
|
|
395
307
|
if (respObjKey === null) {
|
|
396
|
-
|
|
308
|
+
specificResp = transUtilInst.matchResponse(uriPath, method, 'WITHOPTIONS', responseKeys, entitySchema.responseDatatype);
|
|
309
|
+
if (specificResp) {
|
|
310
|
+
respObjKey = specificResp.key;
|
|
311
|
+
}
|
|
397
312
|
}
|
|
398
313
|
}
|
|
399
314
|
|
|
400
315
|
if (respObjKey === null) {
|
|
401
|
-
|
|
316
|
+
const specificResp = transUtilInst.matchResponse(uriPath, method, 'DEFAULT', responseKeys, entitySchema.responseDatatype);
|
|
317
|
+
if (specificResp) {
|
|
318
|
+
respObjKey = specificResp.key;
|
|
319
|
+
}
|
|
402
320
|
}
|
|
403
321
|
|
|
404
322
|
if (respObjKey === null) {
|
|
@@ -524,6 +442,7 @@ function handleRestRequest(request, entityId, entitySchema, callProperties, filt
|
|
|
524
442
|
return callback(retObject);
|
|
525
443
|
});
|
|
526
444
|
}
|
|
445
|
+
|
|
527
446
|
// process the response - parse it
|
|
528
447
|
try {
|
|
529
448
|
retResponse = JSON.parse(resObj.response.trim());
|
package/lib/translatorUtil.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
/* eslint no-use-before-define: warn */
|
|
8
8
|
/* eslint prefer-object-spread:warn */
|
|
9
9
|
/* eslint prefer-destructuring:warn */
|
|
10
|
+
/* eslint no-continue:warn */
|
|
10
11
|
|
|
11
12
|
/* NodeJS internal utilities */
|
|
12
13
|
const fs = require('fs');
|
|
@@ -17,6 +18,16 @@ const AjvCl = require('ajv');
|
|
|
17
18
|
const jsonQuery = require('json-query');
|
|
18
19
|
const cryptoJS = require('crypto-js');
|
|
19
20
|
|
|
21
|
+
// get global strings
|
|
22
|
+
const { globalStrings } = require(path.join(__dirname, 'constants.js'));
|
|
23
|
+
const { pathVarMarker } = globalStrings;
|
|
24
|
+
const { versionMarker } = globalStrings;
|
|
25
|
+
const { basePathMarker } = globalStrings;
|
|
26
|
+
const { withPathVarPrefix } = globalStrings;
|
|
27
|
+
const { urlSplit } = globalStrings;
|
|
28
|
+
const { pathSplit } = globalStrings;
|
|
29
|
+
const { globalFlag } = globalStrings;
|
|
30
|
+
|
|
20
31
|
let id = null;
|
|
21
32
|
let propUtilInst = null;
|
|
22
33
|
let translator = null;
|
|
@@ -943,6 +954,172 @@ class AdapterTranslatorUtil {
|
|
|
943
954
|
return combinedObj;
|
|
944
955
|
}
|
|
945
956
|
|
|
957
|
+
/**
|
|
958
|
+
* @summary Gets the property from call properties or global
|
|
959
|
+
*
|
|
960
|
+
* @function getPathProperty
|
|
961
|
+
* @param {String} globalProperty - global property value
|
|
962
|
+
* @param {String} propertyName - property name to find it in call properties
|
|
963
|
+
*
|
|
964
|
+
*/
|
|
965
|
+
getPathProperty(globalProperty, propertyName, callProperties) {
|
|
966
|
+
const origin = `${this.myid}-translatorUtil-getPathProperty`;
|
|
967
|
+
log.trace(origin);
|
|
968
|
+
|
|
969
|
+
let propertyValue = `${pathSplit}${globalProperty}`;
|
|
970
|
+
if (callProperties && callProperties[propertyName]) {
|
|
971
|
+
propertyValue = `${pathSplit}${callProperties[propertyName]}`;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// remove any double slash that we may have introduced - consistency
|
|
975
|
+
return propertyValue.replace(new RegExp(`${pathSplit}${pathSplit}`, globalFlag), pathSplit);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* @summary Matches the response to the response object to get the key
|
|
980
|
+
*
|
|
981
|
+
* @function matchResponse
|
|
982
|
+
* @param {String} uriPath - original path from the entitypath
|
|
983
|
+
* @param {String} method - method for the request
|
|
984
|
+
* @param {String} type - type for the mock response
|
|
985
|
+
* @param {Object} mockresponses - mock response objects
|
|
986
|
+
*
|
|
987
|
+
*/
|
|
988
|
+
matchResponse(uriPath, method, type, mockresponses, respDatatype) {
|
|
989
|
+
const origin = `${this.myid}-translatorUtil-matchResponse`;
|
|
990
|
+
log.trace(origin);
|
|
991
|
+
|
|
992
|
+
// Go through the mock data keys to find the proper data to return
|
|
993
|
+
for (let p = 0; p < mockresponses.length; p += 1) {
|
|
994
|
+
// is this the mock data for this call
|
|
995
|
+
if (Object.hasOwnProperty.call(mockresponses[p], 'name') && uriPath === mockresponses[p].name
|
|
996
|
+
&& Object.hasOwnProperty.call(mockresponses[p], 'method') && method.toUpperCase() === mockresponses[p].method.toUpperCase()
|
|
997
|
+
&& Object.hasOwnProperty.call(mockresponses[p], 'type') && type.toUpperCase() === mockresponses[p].type.toUpperCase()) {
|
|
998
|
+
// This response best matches the request so return the key and data
|
|
999
|
+
const specificResp = {
|
|
1000
|
+
key: '',
|
|
1001
|
+
status: 'success',
|
|
1002
|
+
code: 200,
|
|
1003
|
+
response: {}
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
if (Object.hasOwnProperty.call(mockresponses[p], 'key')) {
|
|
1007
|
+
specificResp.key = mockresponses[p].key;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
if (Object.hasOwnProperty.call(mockresponses[p], 'response')) {
|
|
1011
|
+
// if a status is defined in the response, use it
|
|
1012
|
+
if (Object.hasOwnProperty.call(mockresponses[p].response, 'response')
|
|
1013
|
+
&& Object.hasOwnProperty.call(mockresponses[p].response, 'status')) {
|
|
1014
|
+
specificResp.code = mockresponses[p].response.status;
|
|
1015
|
+
|
|
1016
|
+
if (respDatatype && respDatatype.toUpperCase() === 'XML2JSON') {
|
|
1017
|
+
specificResp.response = mockresponses[p].response.response;
|
|
1018
|
+
} else {
|
|
1019
|
+
specificResp.response = JSON.stringify(mockresponses[p].response.response);
|
|
1020
|
+
}
|
|
1021
|
+
} else if (respDatatype && respDatatype.toUpperCase() === 'XML2JSON') {
|
|
1022
|
+
// if no response field, assume that the entire data is the response
|
|
1023
|
+
specificResp.response = mockresponses[p].response;
|
|
1024
|
+
} else {
|
|
1025
|
+
specificResp.response = JSON.stringify(mockresponses[p].response);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
return specificResp;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* @summary Gets the response object key from the path for determining how to match response
|
|
1038
|
+
* This might be used by token calls in the connector so it might be good to move out of here!
|
|
1039
|
+
*
|
|
1040
|
+
* @function getRespObjKeyFromPath
|
|
1041
|
+
* @param {String} uriPath - original path from the entitypath
|
|
1042
|
+
* @param {String} reqPath - path after translations - actual call path
|
|
1043
|
+
* @param {Object} callProperties - properties to override on this call
|
|
1044
|
+
* @param {String} basepathGl - base path global property
|
|
1045
|
+
* @param {String} versionGl - version global property
|
|
1046
|
+
* @param {String} method - method type for the request
|
|
1047
|
+
* @param {bollean} withPath - whether we are checking for with path vars
|
|
1048
|
+
* @param {Object} entitySchema - the entity schema for the request
|
|
1049
|
+
*
|
|
1050
|
+
*/
|
|
1051
|
+
getRespObjKeyFromPath(uriPath, reqPath, callProperties, basepathGl, versionGl, method, withPath, entitySchema) {
|
|
1052
|
+
const origin = `${this.myid}-translatorUtil-getRespObjKeyFromPath`;
|
|
1053
|
+
log.trace(origin);
|
|
1054
|
+
let specificResp = null;
|
|
1055
|
+
|
|
1056
|
+
// if there are path variables, see if there is something that matches a specific variable
|
|
1057
|
+
if (uriPath.indexOf(pathVarMarker) >= 0) {
|
|
1058
|
+
const uriTemp = uriPath.split(urlSplit);
|
|
1059
|
+
const actTemp = reqPath.split(urlSplit);
|
|
1060
|
+
|
|
1061
|
+
// make sure the entitypath has a standard format since they can differ (e.g. slashes or no slashes)
|
|
1062
|
+
uriTemp[0] = uriTemp[0].replace(new RegExp(pathVarMarker, globalFlag), `${pathSplit}${pathVarMarker}`);
|
|
1063
|
+
uriTemp[0] = uriTemp[0].replace(new RegExp(versionMarker, globalFlag), `${pathSplit}${versionMarker}`);
|
|
1064
|
+
uriTemp[0] = uriTemp[0].replace(new RegExp(basePathMarker, globalFlag), `${pathSplit}${basePathMarker}`);
|
|
1065
|
+
uriTemp[0] = uriTemp[0].replace(new RegExp(`${pathSplit}${pathSplit}`, globalFlag), pathSplit);
|
|
1066
|
+
|
|
1067
|
+
// if the original path has {base_path}, remove base_path from both paths
|
|
1068
|
+
if (uriTemp[0].indexOf(basePathMarker) >= 0) {
|
|
1069
|
+
// get rid of base path marker from the original path
|
|
1070
|
+
uriTemp[0] = uriTemp[0].replace(new RegExp(`${pathSplit}${basePathMarker}`, globalFlag), '');
|
|
1071
|
+
// get rid of base path from the request path
|
|
1072
|
+
if (basepathGl && basepathGl !== '/') {
|
|
1073
|
+
actTemp[0] = actTemp[0].replace(this.getPathProperty(basepathGl, 'base_path', callProperties), '');
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// if the original path has {version}, remove version from both paths
|
|
1078
|
+
if (uriTemp[0].indexOf(versionMarker) >= 0) {
|
|
1079
|
+
// get rid of version marker from the original path
|
|
1080
|
+
uriTemp[0] = uriTemp[0].replace(new RegExp(`${pathSplit}${versionMarker}`, globalFlag), '');
|
|
1081
|
+
// get rid of version from the request path
|
|
1082
|
+
if (versionGl && versionGl !== '/') {
|
|
1083
|
+
actTemp[0] = actTemp[0].replace(this.getPathProperty(versionGl, 'version', callProperties), '');
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
const uriArray = uriTemp[0].split(pathSplit);
|
|
1088
|
+
const actArray = actTemp[0].split(pathSplit);
|
|
1089
|
+
|
|
1090
|
+
// if this is one of the generic requests need to pad the actual array or mock data for specific path variable will not work
|
|
1091
|
+
if (entitySchema.entitypath.indexOf('/{pathv1}/{pathv2}/{pathv3}/{pathv4}/{pathv5}/{pathv6}/{pathv7}/{pathv8}/{pathv9}/{pathv10}/{pathv11}/{pathv12}/{pathv13}/{pathv14}/{pathv15}/{pathv16}/{pathv17}/{pathv18}/{pathv19}/{pathv20}?{query}') >= 0) {
|
|
1092
|
+
for (let e = actArray.length; e < uriArray.length; e += 1) {
|
|
1093
|
+
actArray.push('');
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// the number of items in both should be the same
|
|
1098
|
+
if (uriArray.length === actArray.length) {
|
|
1099
|
+
let varCount = 1;
|
|
1100
|
+
// want to go through the path to find the path variables
|
|
1101
|
+
for (let i = 0; i < uriArray.length; i += 1) {
|
|
1102
|
+
// if this is a path variable in the original path
|
|
1103
|
+
if (uriArray[i].indexOf(pathVarMarker) >= 0) {
|
|
1104
|
+
// are we checking generic path vars (withPath) or specific path data
|
|
1105
|
+
if (withPath) {
|
|
1106
|
+
specificResp = this.matchResponse(uriPath, method, `${withPathVarPrefix}${varCount}`, entitySchema.mockresponses, entitySchema.responseDatatype);
|
|
1107
|
+
} else {
|
|
1108
|
+
specificResp = this.matchResponse(uriPath, method, actArray[i], entitySchema.mockresponses, entitySchema.responseDatatype);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
if (specificResp !== null) {
|
|
1112
|
+
break;
|
|
1113
|
+
}
|
|
1114
|
+
varCount += 1;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
return specificResp;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
946
1123
|
/**
|
|
947
1124
|
* @summary If the input is a stringified JSON object, return the JSON object. Otherwise,
|
|
948
1125
|
* return the object that was provided.
|