@itentialopensource/adapter-utils 4.44.9
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 +3 -0
- package/.eslintrc.js +18 -0
- package/.jshintrc +3 -0
- package/CHANGELOG.md +1398 -0
- package/CODE_OF_CONDUCT.md +48 -0
- package/CONTRIBUTING.md +173 -0
- package/LICENSE +201 -0
- package/README.md +12 -0
- package/actionSchema.json +186 -0
- package/error.json +148 -0
- package/index.js +7 -0
- package/lib/connectorRest.js +4083 -0
- package/lib/dbUtil.js +1300 -0
- package/lib/propertyUtil.js +1012 -0
- package/lib/requestHandler.js +1175 -0
- package/lib/restHandler.js +1309 -0
- package/lib/throttle.js +1289 -0
- package/lib/translatorUtil.js +1137 -0
- package/package.json +61 -0
- package/propertiesSchema.json +840 -0
- package/utils/pre-commit.sh +26 -0
- package/utils/setup.js +32 -0
- package/utils/testRunner.js +259 -0
|
@@ -0,0 +1,1137 @@
|
|
|
1
|
+
/* @copyright Itential, LLC 2018-9 */
|
|
2
|
+
|
|
3
|
+
// Set globals
|
|
4
|
+
/* global log */
|
|
5
|
+
/* eslint global-require:warn */
|
|
6
|
+
/* eslint import/no-dynamic-require:warn */
|
|
7
|
+
/* eslint no-use-before-define: warn */
|
|
8
|
+
/* eslint prefer-object-spread:warn */
|
|
9
|
+
/* eslint prefer-destructuring:warn */
|
|
10
|
+
|
|
11
|
+
/* NodeJS internal utilities */
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// The schema validator
|
|
16
|
+
const AjvCl = require('ajv');
|
|
17
|
+
const jsonQuery = require('json-query');
|
|
18
|
+
const cryptoJS = require('crypto-js');
|
|
19
|
+
|
|
20
|
+
let id = null;
|
|
21
|
+
let propUtilInst = null;
|
|
22
|
+
let translator = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @summary Takes in text and a key, encodes or if key then encrypts and returns the resulting
|
|
26
|
+
* encoded/encrypted string
|
|
27
|
+
*
|
|
28
|
+
* @function encrypt
|
|
29
|
+
* @param {String} value - the text to encrypt (required)
|
|
30
|
+
* @param {Object} eInfo - the encryption information (optional)
|
|
31
|
+
*
|
|
32
|
+
* @return {String} the encrypted/encoded string
|
|
33
|
+
*/
|
|
34
|
+
function encrypt(value, eInfo) {
|
|
35
|
+
const origin = `${id}-translatorUtil-encrypt`;
|
|
36
|
+
log.trace(origin);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// verify the input for the method
|
|
40
|
+
if (!value) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// if encrypting, return the encrypted string
|
|
45
|
+
if (eInfo && eInfo.key) {
|
|
46
|
+
// this is being added to support different types of encryption
|
|
47
|
+
if (eInfo.type) {
|
|
48
|
+
return `${cryptoJS.AES.encrypt(value, eInfo.key)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return `${cryptoJS.AES.encrypt(value, eInfo.key)}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// if encoding, return the encoded string
|
|
55
|
+
return `${Buffer.from(value).toString('base64')}`;
|
|
56
|
+
} catch (e) {
|
|
57
|
+
log.error(`${origin}: Encyrpt took exception: ${e}`);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @summary Takes in encrypted or encoded text and decodes/decrypts it to return
|
|
64
|
+
* the actual text
|
|
65
|
+
*
|
|
66
|
+
* @function decrypt
|
|
67
|
+
* @param {String} value - the text to decrypt (required)
|
|
68
|
+
* @param {Object} eInfo - the encryption information (optional)
|
|
69
|
+
*
|
|
70
|
+
* @return {String} the string
|
|
71
|
+
*/
|
|
72
|
+
function decrypt(value, eInfo) {
|
|
73
|
+
const origin = `${id}-translatorUtil-decrypt`;
|
|
74
|
+
log.trace(origin);
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
// verify the input for the method
|
|
78
|
+
if (!value) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// if decrypting, return the decrypted string
|
|
83
|
+
if (eInfo && eInfo.key) {
|
|
84
|
+
// this is being added to support different types of encryption
|
|
85
|
+
if (eInfo.type) {
|
|
86
|
+
return cryptoJS.AES.decrypt(value, eInfo.key).toString(cryptoJS.enc.Utf8);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return cryptoJS.AES.decrypt(value, eInfo.key).toString(cryptoJS.enc.Utf8);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// if decoding, return the decoded string
|
|
93
|
+
return `${Buffer.from(value, 'base64').toString('ascii')}`;
|
|
94
|
+
} catch (e) {
|
|
95
|
+
log.error(`${origin}: Decrypt took exception: ${e}`);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @summary Takes in an object and returns the value. If it is an Array, it
|
|
102
|
+
* returns the first element in the array. It returns null if it can not get
|
|
103
|
+
* the value from the object
|
|
104
|
+
*
|
|
105
|
+
* @function getValueFromObject
|
|
106
|
+
* @param {Object} object - the object to extract the data from
|
|
107
|
+
* @param {Object} dataSchema - the schema of data to extract
|
|
108
|
+
* @param {boolean} request - whether this is an outbound request
|
|
109
|
+
* @param {Boolean} dynamicFields - do we show fields not in schema
|
|
110
|
+
*
|
|
111
|
+
* @return {Value} the value we want (String, Boolean or Number)
|
|
112
|
+
*/
|
|
113
|
+
function getValueFromObject(object, dataSchema, request, dynamicFields) {
|
|
114
|
+
const origin = `${id}-translatorUtil-getValueFromObject`;
|
|
115
|
+
log.trace(origin);
|
|
116
|
+
|
|
117
|
+
// if no value found - no object
|
|
118
|
+
if (object === undefined || object === null) {
|
|
119
|
+
log.spam(`${origin}: No object returning null`);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// get the type of data we are working with
|
|
124
|
+
let type = null;
|
|
125
|
+
|
|
126
|
+
if (dataSchema && dataSchema.type && !Array.isArray(dataSchema.type)) {
|
|
127
|
+
type = dataSchema.type.toLowerCase();
|
|
128
|
+
} else if (dataSchema && dataSchema.type && Array.isArray(dataSchema.type)) {
|
|
129
|
+
if (dataSchema.type.length === 1) {
|
|
130
|
+
type = dataSchema.type[0].toLowerCase();
|
|
131
|
+
} else if (dataSchema.type.length > 1) {
|
|
132
|
+
if (typeof object === 'string' && dataSchema.type.toString().toLowerCase().indexOf('string') > -1) {
|
|
133
|
+
type = 'string';
|
|
134
|
+
} else if (typeof object === 'number' && dataSchema.type.toString().toLowerCase().indexOf('number') > -1) {
|
|
135
|
+
type = 'number';
|
|
136
|
+
} else if (typeof object === 'number' && dataSchema.type.toString().toLowerCase().indexOf('integer') > -1) {
|
|
137
|
+
type = 'integer';
|
|
138
|
+
} else if (typeof object === 'boolean' && dataSchema.type.toString().toLowerCase().indexOf('boolean') > -1) {
|
|
139
|
+
type = 'boolean';
|
|
140
|
+
} else if (Array.isArray(object) && dataSchema.type.toString().toLowerCase().indexOf('array') > -1) {
|
|
141
|
+
type = 'array';
|
|
142
|
+
} else if (typeof object === 'object' && dataSchema.type.toString().toLowerCase().indexOf('object') > -1) {
|
|
143
|
+
type = 'object';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// if there is no type on the data, just send it through
|
|
149
|
+
if (!type) {
|
|
150
|
+
return object;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// if we are supposed to extract a number, boolean or string
|
|
154
|
+
if (type === 'number' || type === 'integer' || type === 'boolean' || type === 'string') {
|
|
155
|
+
// if data is an array, just return the first object
|
|
156
|
+
if (Array.isArray(object)) {
|
|
157
|
+
if (object.length === 0 || object[0] === '') {
|
|
158
|
+
log.spam(`${origin}: No value returning null`);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return object[0];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (typeof object === 'string' && object === '') {
|
|
166
|
+
log.spam(`${origin}: Empty value returning null`);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return object;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// if extracting an array, need recursion to handle elements in the array
|
|
174
|
+
if (type === 'array') {
|
|
175
|
+
// if data is not an array - then can not work an array
|
|
176
|
+
if (!Array.isArray(object)) {
|
|
177
|
+
log.spam(`${origin}: Data is not an array returning null`);
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const returnArr = [];
|
|
182
|
+
|
|
183
|
+
// loop through all of the elements in the array
|
|
184
|
+
for (let k = 0; k < object.length; k += 1) {
|
|
185
|
+
// if the array is supposed to have items (obect within it)
|
|
186
|
+
if (dataSchema.items) {
|
|
187
|
+
// recursive call to get array elements
|
|
188
|
+
const fieldValue = getValueFromObject(object[k], dataSchema.items, request, dynamicFields);
|
|
189
|
+
|
|
190
|
+
// if data to return, add to the array being returned
|
|
191
|
+
if (fieldValue !== null) {
|
|
192
|
+
returnArr.push(fieldValue);
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
// just add elements to the array to be returned
|
|
196
|
+
returnArr.push(object[k]);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// if nothing in the array return null
|
|
201
|
+
if (returnArr.length === 0) {
|
|
202
|
+
log.spam(`${origin}: No data to return returning null`);
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return returnArr;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// if extracting an object, need recursion to handle properties in the object
|
|
210
|
+
if (type === 'object') {
|
|
211
|
+
let returnObj = {};
|
|
212
|
+
|
|
213
|
+
if (request) {
|
|
214
|
+
returnObj = buildObject(object, dataSchema, dynamicFields);
|
|
215
|
+
} else {
|
|
216
|
+
returnObj = extractObject(object, dataSchema, dynamicFields);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// if nothing in the array return null
|
|
220
|
+
if (Object.keys(returnObj).length === 0) {
|
|
221
|
+
return {};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return returnObj;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// unsupported type - return null
|
|
228
|
+
log.spam(`${origin}: Unsupported data type returning null`);
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* INTERNAL FUNCTION
|
|
234
|
+
*
|
|
235
|
+
* @summary Takes in a JSON object containing an entity it then extracts the info
|
|
236
|
+
* IAP cares about into a IAP Entity
|
|
237
|
+
*
|
|
238
|
+
* @function extractObject
|
|
239
|
+
* @param {Object} dataObj - the object from the other system
|
|
240
|
+
* @param {String} entitySchema - the entity schema
|
|
241
|
+
* @param {Boolean} dynamicFields - do we show fields not in schema
|
|
242
|
+
*
|
|
243
|
+
* @return {Object} the IAP Entity
|
|
244
|
+
*/
|
|
245
|
+
function extractObject(dataObj, entitySchema, dynamicFields) {
|
|
246
|
+
const origin = `${id}-translatorUtil-extractObject`;
|
|
247
|
+
log.trace(origin);
|
|
248
|
+
const returnObj = {};
|
|
249
|
+
let addFields = dynamicFields;
|
|
250
|
+
|
|
251
|
+
// if no translation needed - just return the object
|
|
252
|
+
if (Object.hasOwnProperty.call(entitySchema, 'translate')
|
|
253
|
+
&& typeof entitySchema.translate === 'boolean' && entitySchema.translate === false) {
|
|
254
|
+
return dataObj;
|
|
255
|
+
}
|
|
256
|
+
// Should allow dymanic fields on this object? - change to inherited
|
|
257
|
+
if (Object.hasOwnProperty.call(entitySchema, 'dynamicfields')
|
|
258
|
+
&& typeof entitySchema.dynamicfields === 'boolean') {
|
|
259
|
+
addFields = entitySchema.dynamicfields;
|
|
260
|
+
}
|
|
261
|
+
// if there are no properties - if addFields is true return object
|
|
262
|
+
if (!entitySchema.properties && (addFields)) {
|
|
263
|
+
return dataObj;
|
|
264
|
+
}
|
|
265
|
+
// if there are no properties and no dynamic fields, return null
|
|
266
|
+
if (!entitySchema.properties) {
|
|
267
|
+
return returnObj;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const schemaKeys = Object.keys(entitySchema.properties);
|
|
271
|
+
|
|
272
|
+
// loop through all of the properties in the schema
|
|
273
|
+
for (let k = 0; k < schemaKeys.length; k += 1) {
|
|
274
|
+
const field = entitySchema.properties[schemaKeys[k]];
|
|
275
|
+
|
|
276
|
+
// if this property has an external name, need to see if in the data object
|
|
277
|
+
if (Object.hasOwnProperty.call(field, 'external_name')) {
|
|
278
|
+
// if the external name is something get that field
|
|
279
|
+
if (field.external_name) {
|
|
280
|
+
// need to determine the field in the incoming object where the data is
|
|
281
|
+
const externalPath = field.external_name.split('.');
|
|
282
|
+
let location = dataObj;
|
|
283
|
+
let inField = null;
|
|
284
|
+
|
|
285
|
+
// get to the field in the object
|
|
286
|
+
for (let a = 0; a < externalPath.length; a += 1) {
|
|
287
|
+
// if we are at the point to get the data, get the data
|
|
288
|
+
if (a === externalPath.length - 1) {
|
|
289
|
+
inField = location[externalPath[a]];
|
|
290
|
+
} else if (location[externalPath[a]]) {
|
|
291
|
+
// walk down the object path, if it exists
|
|
292
|
+
location = location[externalPath[a]];
|
|
293
|
+
} else {
|
|
294
|
+
// if the path does not exist, break the loop and value is null
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// get the field value from the data so it can be put in the return
|
|
300
|
+
let fieldValue = getValueFromObject(inField, field, false, addFields);
|
|
301
|
+
|
|
302
|
+
// if we are decoding/decrypting the value
|
|
303
|
+
if (field.encode || (field.encrypt && field.encrypt.key)) {
|
|
304
|
+
fieldValue = decrypt(fieldValue, field.encrypt);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// if in the data object, add to the IAP entity
|
|
308
|
+
if (fieldValue !== null) {
|
|
309
|
+
if (field.respFilter) {
|
|
310
|
+
returnObj[schemaKeys[k]] = jsonQuery(field.respFilter, { data: fieldValue }).value;
|
|
311
|
+
} else {
|
|
312
|
+
returnObj[schemaKeys[k]] = fieldValue;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// if the external field is null or empty then we ignore the field!
|
|
317
|
+
} else {
|
|
318
|
+
// if the field does not have an external name then use field key
|
|
319
|
+
let fieldValue = getValueFromObject(dataObj[schemaKeys[k]], field, false, addFields);
|
|
320
|
+
|
|
321
|
+
// if we are decoding/decrypting the value
|
|
322
|
+
if (field.encode || (field.encrypt && field.encrypt.key)) {
|
|
323
|
+
fieldValue = decrypt(fieldValue, field.encrypt);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// if in the data object, add to the IAP entity
|
|
327
|
+
if (fieldValue !== null) {
|
|
328
|
+
if (field.respFilter) {
|
|
329
|
+
returnObj[schemaKeys[k]] = jsonQuery(field.respFilter, { data: fieldValue }).value;
|
|
330
|
+
} else {
|
|
331
|
+
returnObj[schemaKeys[k]] = fieldValue;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// if we should allow dymanic fields on this object, add the fields
|
|
338
|
+
if (addFields) {
|
|
339
|
+
const objectKeys = Object.keys(dataObj);
|
|
340
|
+
|
|
341
|
+
// loop through all of the fields in the object
|
|
342
|
+
for (let o = 0; o < objectKeys.length; o += 1) {
|
|
343
|
+
let found = false;
|
|
344
|
+
|
|
345
|
+
// loop through all of the properties in the schema
|
|
346
|
+
for (let en = 0; en < schemaKeys.length; en += 1) {
|
|
347
|
+
const field = entitySchema.properties[schemaKeys[en]];
|
|
348
|
+
|
|
349
|
+
// if the field is not in the schema - we need to add it
|
|
350
|
+
// using the field name that came in since no translation
|
|
351
|
+
if (field.external_name) {
|
|
352
|
+
const externalPath = field.external_name.split('.');
|
|
353
|
+
|
|
354
|
+
if (externalPath[externalPath.length - 1] === objectKeys[o]) {
|
|
355
|
+
found = true;
|
|
356
|
+
}
|
|
357
|
+
} else if (schemaKeys[en] === objectKeys[o]) {
|
|
358
|
+
found = true;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// if not found, add it
|
|
363
|
+
if (!found) {
|
|
364
|
+
returnObj[objectKeys[o]] = dataObj[objectKeys[o]];
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// return the resulting IAP entity
|
|
370
|
+
return returnObj;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* @summary Checks to see if any fields in the schema need to be parsed
|
|
375
|
+
*
|
|
376
|
+
* @function parseFields
|
|
377
|
+
* @param {Object} retObject - the object to extract the data from
|
|
378
|
+
* @param {Array} dataSchema - the schema of data to extract
|
|
379
|
+
*
|
|
380
|
+
* @return {Object} the return object with parsed data
|
|
381
|
+
*/
|
|
382
|
+
function parseFields(retObject, dataSchema) {
|
|
383
|
+
const origin = `${id}-translatorUtil-parseFields`;
|
|
384
|
+
log.trace(origin);
|
|
385
|
+
const myReturn = retObject;
|
|
386
|
+
const schemaKeys = Object.keys(dataSchema);
|
|
387
|
+
|
|
388
|
+
// loop through all of the properties in the schema
|
|
389
|
+
for (let k = 0; k < schemaKeys.length; k += 1) {
|
|
390
|
+
const field = dataSchema[schemaKeys[k]];
|
|
391
|
+
|
|
392
|
+
// if object or array need rercursion
|
|
393
|
+
if (field.type === 'object' && field.properties && myReturn && myReturn[schemaKeys[k]]) {
|
|
394
|
+
myReturn[schemaKeys[k]] = parseFields(myReturn[schemaKeys[k]], field.properties);
|
|
395
|
+
}
|
|
396
|
+
if (field.type === 'array' && field.items && myReturn && myReturn[schemaKeys[k]]) {
|
|
397
|
+
myReturn[schemaKeys[k]] = parseFields(myReturn[schemaKeys[k]], field.items);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// if string and we need to parse this field
|
|
401
|
+
if (field.type === 'string' && field.parse && myReturn && myReturn[schemaKeys[k]]) {
|
|
402
|
+
try {
|
|
403
|
+
myReturn[schemaKeys[k]] = JSON.parse(myReturn[schemaKeys[k]]);
|
|
404
|
+
} catch (ex) {
|
|
405
|
+
log.warn(`${origin}: Could not parse data in field`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return myReturn;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* INTERNAL FUNCTION
|
|
414
|
+
*
|
|
415
|
+
* @summary Takes in a JSON object containing an entity it then extracts the info
|
|
416
|
+
* IAP cares about into a IAP Entity. This object is then merged with
|
|
417
|
+
* default data and validated against the provided entity schema.
|
|
418
|
+
*
|
|
419
|
+
* @function extractJSONEntity
|
|
420
|
+
* @param {Object} dataObj - the object from the other system
|
|
421
|
+
* @param {String} entitySchema - the entity schema
|
|
422
|
+
*
|
|
423
|
+
* @return {Object} the IAP Entity
|
|
424
|
+
*/
|
|
425
|
+
function extractJSONEntity(dataObj, entitySchema) {
|
|
426
|
+
const origin = `${id}-translatorUtil-extractJSONEntity`;
|
|
427
|
+
log.trace(origin);
|
|
428
|
+
const returnObj = extractObject(dataObj, entitySchema, false);
|
|
429
|
+
|
|
430
|
+
try {
|
|
431
|
+
// add any defaults to the data
|
|
432
|
+
let combinedEntity = propUtilInst.mergeProperties(returnObj, propUtilInst.setDefaults(entitySchema));
|
|
433
|
+
|
|
434
|
+
// validate the entity against the schema
|
|
435
|
+
const ajvInst = new AjvCl();
|
|
436
|
+
const validate = ajvInst.compile(entitySchema);
|
|
437
|
+
const result = validate(combinedEntity);
|
|
438
|
+
|
|
439
|
+
// if invalid properties throw an error
|
|
440
|
+
if (!result) {
|
|
441
|
+
// create the generic part of an error object
|
|
442
|
+
const errorObj = {
|
|
443
|
+
origin,
|
|
444
|
+
type: 'Schema Validation Failure',
|
|
445
|
+
vars: [validate.errors[0].message]
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// log and throw the error
|
|
449
|
+
log.error(`${origin}: Schema validation failure ${validate.errors[0].message}`);
|
|
450
|
+
throw new Error(JSON.stringify(errorObj));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// if IAP request type exists, remove it (internal use only)
|
|
454
|
+
if (combinedEntity.ph_request_type) {
|
|
455
|
+
delete combinedEntity.ph_request_type;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// see if we need to parse fields
|
|
459
|
+
// only if translating
|
|
460
|
+
if (entitySchema.translate) {
|
|
461
|
+
// must have properties
|
|
462
|
+
if (entitySchema.properties) {
|
|
463
|
+
// get the schema keys
|
|
464
|
+
combinedEntity = parseFields(combinedEntity, entitySchema.properties);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// return the resulting IAP entity
|
|
469
|
+
return combinedEntity;
|
|
470
|
+
} catch (e) {
|
|
471
|
+
return translator.checkAndThrow(e, origin, 'Issue extracting JSON');
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* INTERNAL FUNCTION
|
|
477
|
+
*
|
|
478
|
+
* @summary Takes in a IAP object containing an entity it then extracts the info
|
|
479
|
+
* the external system cares about into a System Entity
|
|
480
|
+
*
|
|
481
|
+
* @function buildJSONEntity
|
|
482
|
+
* @param {Object} dataObj - the object from IAP
|
|
483
|
+
* @param {String} entitySchema - the entity schema
|
|
484
|
+
* @param {Boolean} dynamicFields - do we show fields not in schema
|
|
485
|
+
*
|
|
486
|
+
* @return {Object} the Entity for the other system
|
|
487
|
+
*/
|
|
488
|
+
function buildObject(dataObj, entitySchema, dynamicFields) {
|
|
489
|
+
const origin = `${id}-translatorUtil-buildObject`;
|
|
490
|
+
log.trace(origin);
|
|
491
|
+
|
|
492
|
+
const returnObj = {};
|
|
493
|
+
let addFields = dynamicFields;
|
|
494
|
+
|
|
495
|
+
// if no translation needed - just return the object
|
|
496
|
+
if (Object.hasOwnProperty.call(entitySchema, 'translate')
|
|
497
|
+
&& typeof entitySchema.translate === 'boolean' && entitySchema.translate === false) {
|
|
498
|
+
return dataObj;
|
|
499
|
+
}
|
|
500
|
+
// Should allow dymanic fields on this object? - change to inherited
|
|
501
|
+
if (Object.hasOwnProperty.call(entitySchema, 'dynamicfields')
|
|
502
|
+
&& typeof entitySchema.dynamicfields === 'boolean') {
|
|
503
|
+
addFields = entitySchema.dynamicfields;
|
|
504
|
+
}
|
|
505
|
+
// if there are no properties - if addFields is true return object
|
|
506
|
+
if (!entitySchema.properties && addFields) {
|
|
507
|
+
return dataObj;
|
|
508
|
+
}
|
|
509
|
+
// if there are no properties and no dynamic fields, return null
|
|
510
|
+
if (!entitySchema.properties) {
|
|
511
|
+
return returnObj;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const schemaKeys = Object.keys(entitySchema.properties);
|
|
515
|
+
|
|
516
|
+
// loop through all of the properties in the schema
|
|
517
|
+
for (let k = 0; k < schemaKeys.length; k += 1) {
|
|
518
|
+
const field = entitySchema.properties[schemaKeys[k]];
|
|
519
|
+
|
|
520
|
+
// if this property has an external name, need to see if in the data object
|
|
521
|
+
if (Object.hasOwnProperty.call(field, 'external_name')) {
|
|
522
|
+
// if the external name is something get that field
|
|
523
|
+
if (field.external_name) {
|
|
524
|
+
let fieldValue = getValueFromObject(dataObj[schemaKeys[k]], field, true, addFields);
|
|
525
|
+
|
|
526
|
+
// if we are encoding/encrypting the value
|
|
527
|
+
if (field.encode || (field.encrypt && field.encrypt.key)) {
|
|
528
|
+
fieldValue = encrypt(fieldValue, field.encrypt);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// if in the data object, add to the system entity
|
|
532
|
+
if (fieldValue !== null) {
|
|
533
|
+
// need to determine the field in the object where the data should go
|
|
534
|
+
const externalPath = field.external_name.split('.');
|
|
535
|
+
let location = returnObj;
|
|
536
|
+
|
|
537
|
+
// get to the field in the object
|
|
538
|
+
for (let a = 0; a < externalPath.length; a += 1) {
|
|
539
|
+
let isArray = false;
|
|
540
|
+
let tind = 0;
|
|
541
|
+
let epath = externalPath[a];
|
|
542
|
+
if (epath.indexOf('[') >= 0) {
|
|
543
|
+
isArray = true;
|
|
544
|
+
const epathparts = epath.split('[');
|
|
545
|
+
epath = epathparts[0];
|
|
546
|
+
const endI = epathparts[1].indexOf(']');
|
|
547
|
+
tind = Number(epathparts[1].substring(0, endI));
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// if we are at the point to put the data - set it
|
|
551
|
+
if (a === externalPath.length - 1) {
|
|
552
|
+
// if we already have this key in the object, need to merge
|
|
553
|
+
if (location[epath] && typeof location[epath] === 'object') {
|
|
554
|
+
if (isArray) {
|
|
555
|
+
log.warn(`${epath} is already in the data as an object - check data for errors`);
|
|
556
|
+
}
|
|
557
|
+
const baseObj = {};
|
|
558
|
+
baseObj[epath] = fieldValue;
|
|
559
|
+
|
|
560
|
+
// this will merge the two objects - first object is default
|
|
561
|
+
this.mergeObjects(baseObj, location[epath]);
|
|
562
|
+
} else if (location[epath] && Array.isArray(location[epath])) {
|
|
563
|
+
if (!isArray) {
|
|
564
|
+
log.warn(`${epath} is already in the data as an array - check data for errors`);
|
|
565
|
+
}
|
|
566
|
+
// just push the data into the array
|
|
567
|
+
location[epath].push(fieldValue);
|
|
568
|
+
} else if (location[epath]) {
|
|
569
|
+
if (isArray) {
|
|
570
|
+
log.warn(`${epath} is already in the data as a field - check data for errors`);
|
|
571
|
+
}
|
|
572
|
+
location[epath] = fieldValue;
|
|
573
|
+
} else {
|
|
574
|
+
// just put the value in the return object
|
|
575
|
+
location[epath] = fieldValue;
|
|
576
|
+
}
|
|
577
|
+
} else if (location[epath]) {
|
|
578
|
+
// if type does not match then it is an issue - Array v Object
|
|
579
|
+
if ((isArray && typeof location[epath] === 'object') || (!isArray && Array.isArray(location[epath]))) {
|
|
580
|
+
log.warn(`${epath} is already in the data and the type does not match the desired item - check data for errors`);
|
|
581
|
+
}
|
|
582
|
+
// if an array need to make sure the index exists and walk down the array
|
|
583
|
+
if (isArray && Array.isArray(location[epath])) {
|
|
584
|
+
// if the index has not been created yet - create it
|
|
585
|
+
for (let i = location[epath].length; i <= tind; i += 1) {
|
|
586
|
+
location[epath].push({});
|
|
587
|
+
}
|
|
588
|
+
location = location[epath][tind];
|
|
589
|
+
} else {
|
|
590
|
+
// walk down the object path, if it exists
|
|
591
|
+
location = location[epath];
|
|
592
|
+
}
|
|
593
|
+
} else if (isArray) {
|
|
594
|
+
// if the array does not exist yet, create it
|
|
595
|
+
location[epath] = [];
|
|
596
|
+
for (let i = 0; i <= tind; i += 1) {
|
|
597
|
+
location[epath].push({});
|
|
598
|
+
}
|
|
599
|
+
location = location[epath][tind];
|
|
600
|
+
} else {
|
|
601
|
+
// if the sub object does not exist yet, create it
|
|
602
|
+
location[epath] = {};
|
|
603
|
+
location = location[epath];
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
// if the external field is null or empty then we ignore the field!
|
|
609
|
+
} else {
|
|
610
|
+
// if the field does not have an external name then use field key
|
|
611
|
+
let fieldValue = getValueFromObject(dataObj[schemaKeys[k]], field, true, addFields);
|
|
612
|
+
|
|
613
|
+
// if we are encoding/encrypting the value
|
|
614
|
+
if (field.encode || (field.encrypt && field.encrypt.key)) {
|
|
615
|
+
fieldValue = encrypt(fieldValue, field.encrypt);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// if in the data object, add to the IAP entity
|
|
619
|
+
if (fieldValue !== null) {
|
|
620
|
+
// if we already have this key in the object, need to merge
|
|
621
|
+
if (returnObj[schemaKeys[k]] && typeof returnObj[schemaKeys[k]] === 'object') {
|
|
622
|
+
const baseObj = {};
|
|
623
|
+
baseObj[schemaKeys[k]] = fieldValue;
|
|
624
|
+
|
|
625
|
+
// this will merge the two objects - first object is default
|
|
626
|
+
this.mergeObjects(baseObj, returnObj[schemaKeys[k]]);
|
|
627
|
+
} else {
|
|
628
|
+
// just put the value in the return object
|
|
629
|
+
returnObj[schemaKeys[k]] = fieldValue;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// if we should allow dymanic fields on this object, add the fields
|
|
636
|
+
if (addFields) {
|
|
637
|
+
const objectKeys = Object.keys(dataObj);
|
|
638
|
+
|
|
639
|
+
// loop through all of the fields in the object
|
|
640
|
+
for (let o = 0; o < objectKeys.length; o += 1) {
|
|
641
|
+
// if the field is not in the schema - we need to add it
|
|
642
|
+
// using the field name that came in since no translation
|
|
643
|
+
if (!schemaKeys.includes(objectKeys[o])) {
|
|
644
|
+
returnObj[objectKeys[o]] = dataObj[objectKeys[o]];
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// return the resulting system entity
|
|
650
|
+
return returnObj;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* INTERNAL FUNCTION
|
|
655
|
+
*
|
|
656
|
+
* @summary Takes in a IAP object containing an entity. This object is then merged with
|
|
657
|
+
* default data and validated against the provided entity schema. It then extracts the info
|
|
658
|
+
* the external system cares about into a System Entity
|
|
659
|
+
*
|
|
660
|
+
* @function buildJSONEntity
|
|
661
|
+
* @param {Object} dataObj - the object from IAP
|
|
662
|
+
* @param {String} entitySchema - the entity schema
|
|
663
|
+
*
|
|
664
|
+
* @return {Object} the Entity for the other system
|
|
665
|
+
*/
|
|
666
|
+
function buildJSONEntity(dataObj, entitySchema) {
|
|
667
|
+
const origin = `${id}-translatorUtil-buildJSONEntity`;
|
|
668
|
+
log.trace(origin);
|
|
669
|
+
|
|
670
|
+
try {
|
|
671
|
+
// add any defaults to the data
|
|
672
|
+
const combinedEntity = propUtilInst.mergeProperties(dataObj, propUtilInst.setDefaults(entitySchema));
|
|
673
|
+
|
|
674
|
+
// validate the entity against the schema
|
|
675
|
+
const ajvInst = new AjvCl();
|
|
676
|
+
const validate = ajvInst.compile(entitySchema);
|
|
677
|
+
const result = validate(combinedEntity);
|
|
678
|
+
|
|
679
|
+
// if invalid properties throw an error
|
|
680
|
+
if (!result) {
|
|
681
|
+
// create the error object
|
|
682
|
+
const errorObj = {
|
|
683
|
+
origin,
|
|
684
|
+
type: 'Schema Validation Failure',
|
|
685
|
+
vars: [validate.errors[0].message]
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
// log and throw the error
|
|
689
|
+
log.error(`${origin}: Schema validation failure ${validate.errors[0].message}`);
|
|
690
|
+
throw new Error(JSON.stringify(errorObj));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// if IAP request type exists, remove it (internal use only)
|
|
694
|
+
if (combinedEntity.ph_request_type) {
|
|
695
|
+
delete combinedEntity.ph_request_type;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const returnObj = buildObject(combinedEntity, entitySchema, false);
|
|
699
|
+
|
|
700
|
+
// return the resulting system entity
|
|
701
|
+
return returnObj;
|
|
702
|
+
} catch (e) {
|
|
703
|
+
return translator.checkAndThrow(e, origin, 'Issue extracting JSON');
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
class AdapterTranslatorUtil {
|
|
708
|
+
/**
|
|
709
|
+
* Adapter Translator Utility
|
|
710
|
+
* @constructor
|
|
711
|
+
*/
|
|
712
|
+
constructor(prongId, propUtilCl) {
|
|
713
|
+
id = prongId;
|
|
714
|
+
this.myid = prongId;
|
|
715
|
+
this.propUtil = propUtilCl;
|
|
716
|
+
|
|
717
|
+
// set globals (available to private functions)
|
|
718
|
+
propUtilInst = this.propUtil;
|
|
719
|
+
translator = this;
|
|
720
|
+
|
|
721
|
+
// get the path for the specific error file
|
|
722
|
+
const errorFile = path.join(this.propUtil.baseDir, '/error.json');
|
|
723
|
+
|
|
724
|
+
// if the file does not exist - error
|
|
725
|
+
if (!fs.existsSync(errorFile)) {
|
|
726
|
+
const origin = `${this.myid}-translatorUtil-constructor`;
|
|
727
|
+
log.warn(`${origin}: Could not locate ${errorFile} - errors will be missing details`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Read the action from the file system
|
|
731
|
+
this.errors = JSON.parse(fs.readFileSync(errorFile, 'utf-8'));
|
|
732
|
+
this.errors = this.errors.errors;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// GENERIC UTILITY CALLS USED BY VARIOUS TRANSLATORS
|
|
736
|
+
/**
|
|
737
|
+
* @summary Takes in a JSON object containing an entity it then extracts the info
|
|
738
|
+
* IAP cares about into a IAP Entity
|
|
739
|
+
*
|
|
740
|
+
* @function mapFromOutboundEntity
|
|
741
|
+
* @param {Object} inEntity - the entity from the other system
|
|
742
|
+
* @param {String} entitySchema - the entity schema
|
|
743
|
+
*
|
|
744
|
+
* @return {Object} the Entity for use in IAP
|
|
745
|
+
*/
|
|
746
|
+
mapFromOutboundEntity(inEntity, entitySchema) {
|
|
747
|
+
const origin = `${this.myid}-translatorUtil-mapFromOutboundEntity`;
|
|
748
|
+
log.trace(origin);
|
|
749
|
+
|
|
750
|
+
// create the generic part of an error object
|
|
751
|
+
const errorObj = {
|
|
752
|
+
origin
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
try {
|
|
756
|
+
// if nothing passed in, nothing to do
|
|
757
|
+
if (inEntity === null) {
|
|
758
|
+
// add the specific pieces of the error object
|
|
759
|
+
errorObj.type = 'Missing Data';
|
|
760
|
+
errorObj.vars = ['Entity'];
|
|
761
|
+
|
|
762
|
+
// log and throw the error
|
|
763
|
+
log.error(`${origin}: No Entity for mapFromOutboundEntity`);
|
|
764
|
+
throw new Error(JSON.stringify(errorObj));
|
|
765
|
+
}
|
|
766
|
+
if (entitySchema === null) {
|
|
767
|
+
// add the specific pieces of the error object
|
|
768
|
+
errorObj.type = 'Missing Data';
|
|
769
|
+
errorObj.vars = ['Entity Schema'];
|
|
770
|
+
|
|
771
|
+
// log and throw the error
|
|
772
|
+
log.error(`${origin}: No Entity Schema for mapFromOutboundEntity`);
|
|
773
|
+
throw new Error(JSON.stringify(errorObj));
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// if no translation needed on top object - just return the object
|
|
777
|
+
if (Object.hasOwnProperty.call(entitySchema, 'translate')
|
|
778
|
+
&& typeof entitySchema.translate === 'boolean' && entitySchema.translate === false) {
|
|
779
|
+
const cleanEntity = inEntity;
|
|
780
|
+
|
|
781
|
+
// if IAP request type exists, remove it (internal use only)
|
|
782
|
+
if (cleanEntity.ph_request_type) {
|
|
783
|
+
delete cleanEntity.ph_request_type;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return cleanEntity;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// make sure we are working with a JSON object instead of strigified object
|
|
790
|
+
const transObj = this.formatInputData(inEntity);
|
|
791
|
+
|
|
792
|
+
// if an array of Entities, just translate the data (no objects)
|
|
793
|
+
if (Array.isArray(transObj)) {
|
|
794
|
+
const outEntities = [];
|
|
795
|
+
|
|
796
|
+
for (let i = 0; i < transObj.length; i += 1) {
|
|
797
|
+
// is this just an array of data or something that needs to be translated?
|
|
798
|
+
if (typeof transObj[i] === 'object') {
|
|
799
|
+
// move the fields we care about into a IAP Entity Object
|
|
800
|
+
outEntities.push(extractJSONEntity(transObj[i], entitySchema));
|
|
801
|
+
} else {
|
|
802
|
+
outEntities.push(transObj[i]);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
return outEntities;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// if a single System Entity, should translate data and get value for objects
|
|
810
|
+
// move the fields we care about into a IAP Entity Object
|
|
811
|
+
return extractJSONEntity(transObj, entitySchema);
|
|
812
|
+
} catch (e) {
|
|
813
|
+
return this.checkAndThrow(e, origin, 'Issue mapping from outbound entity');
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* @summary Takes in a JSON object containing a IAP entity it then extracts the info
|
|
819
|
+
* the other system cares about into an Entity
|
|
820
|
+
*
|
|
821
|
+
* @function MapToJSONEntity
|
|
822
|
+
* @param {Object} outEntity - the entity to the other system
|
|
823
|
+
* @param {String} entitySchema - the entity schema
|
|
824
|
+
*
|
|
825
|
+
* @return {Object} the Entity for use in the other system
|
|
826
|
+
*/
|
|
827
|
+
mapToOutboundEntity(outEntity, entitySchema) {
|
|
828
|
+
const origin = `${this.myid}-translatorUtil-mapToOutboundEntity`;
|
|
829
|
+
log.trace(origin);
|
|
830
|
+
|
|
831
|
+
// create the generic part of an error object
|
|
832
|
+
const errorObj = {
|
|
833
|
+
origin
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
try {
|
|
837
|
+
// if nothing passed in, nothing to do
|
|
838
|
+
if (outEntity === null) {
|
|
839
|
+
// add the specific pieces of the error object
|
|
840
|
+
errorObj.type = 'Missing Data';
|
|
841
|
+
errorObj.vars = ['Entity'];
|
|
842
|
+
|
|
843
|
+
// log and throw the error
|
|
844
|
+
log.error(`${origin}: No Entity for mapToOutboundEntity`);
|
|
845
|
+
throw new Error(JSON.stringify(errorObj));
|
|
846
|
+
}
|
|
847
|
+
if (!entitySchema) {
|
|
848
|
+
// add the specific pieces of the error object
|
|
849
|
+
errorObj.type = 'Missing Data';
|
|
850
|
+
errorObj.vars = ['Entity Schema'];
|
|
851
|
+
|
|
852
|
+
// log and throw the error
|
|
853
|
+
log.error(`${origin}: No Entity Schema for mapToOutboundEntity`);
|
|
854
|
+
throw new Error(JSON.stringify(errorObj));
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// if no translation needed on top object - just return the object
|
|
858
|
+
if (Object.hasOwnProperty.call(entitySchema, 'translate')
|
|
859
|
+
&& typeof entitySchema.translate === 'boolean' && entitySchema.translate === false) {
|
|
860
|
+
const cleanEntity = outEntity;
|
|
861
|
+
|
|
862
|
+
// if IAP request type exists, remove it (internal use only)
|
|
863
|
+
if (cleanEntity.ph_request_type) {
|
|
864
|
+
delete cleanEntity.ph_request_type;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
return cleanEntity;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// make sure we are working with a JSON object instead of strigified object
|
|
871
|
+
const transObj = this.formatInputData(outEntity);
|
|
872
|
+
|
|
873
|
+
// if this is an array of Objects, translate each of the objects
|
|
874
|
+
if (Array.isArray(transObj)) {
|
|
875
|
+
const retObjects = [];
|
|
876
|
+
|
|
877
|
+
for (let i = 0; i < transObj.length; i += 1) {
|
|
878
|
+
// move the fields the other system cares about into the Object
|
|
879
|
+
retObjects.push(buildJSONEntity(transObj[i], entitySchema));
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return retObjects;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// if a single Object, should translate data and get value for objects
|
|
886
|
+
// move the fields the other system cares about into the Object
|
|
887
|
+
return buildJSONEntity(transObj, entitySchema);
|
|
888
|
+
} catch (e) {
|
|
889
|
+
return this.checkAndThrow(e, origin, 'Issue mapping to outbound entity');
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* @summary Takes in two objects and merges them so the returned
|
|
895
|
+
* object has secondary only where no primary values were provided.
|
|
896
|
+
*
|
|
897
|
+
* @function mergeObjects
|
|
898
|
+
* @param {Object} object - the primary object
|
|
899
|
+
* @param {Object} secondary - the secondary object
|
|
900
|
+
*
|
|
901
|
+
* @return {Object} the properties with the merged in secondaries
|
|
902
|
+
*/
|
|
903
|
+
mergeObjects(object, secondary) {
|
|
904
|
+
const origin = `${this.myid}-translatorUtil-mergeObjects`;
|
|
905
|
+
log.trace(origin);
|
|
906
|
+
|
|
907
|
+
// do not waste time merging if there is nothing to merge
|
|
908
|
+
if (!object || typeof object !== 'object') {
|
|
909
|
+
return Object.assign({}, secondary);
|
|
910
|
+
}
|
|
911
|
+
if (!secondary || typeof secondary !== 'object') {
|
|
912
|
+
return Object.assign({}, object);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
const combinedObj = Object.assign({}, secondary);
|
|
916
|
+
const keys = Object.keys(object);
|
|
917
|
+
|
|
918
|
+
// loop through all of the primary object to insert them into the conbined data
|
|
919
|
+
for (let k = 0; k < keys.length; k += 1) {
|
|
920
|
+
const thisField = object[keys[k]];
|
|
921
|
+
|
|
922
|
+
// if this key is to an object
|
|
923
|
+
if (thisField && typeof thisField === 'object' && combinedObj[keys[k]]) {
|
|
924
|
+
// recursive call with primary and secondary object
|
|
925
|
+
combinedObj[keys[k]] = this.mergeObjects(thisField, combinedObj[keys[k]]);
|
|
926
|
+
} else if (thisField || !combinedObj[keys[k]]) {
|
|
927
|
+
// if no secondary or primary has value merge it - overriding the secondary
|
|
928
|
+
combinedObj[keys[k]] = thisField;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// return the merged object
|
|
933
|
+
return combinedObj;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* @summary If the input is a stringified JSON object, return the JSON object. Otherwise,
|
|
938
|
+
* return the object that was provided.
|
|
939
|
+
*
|
|
940
|
+
* @function formatInputData
|
|
941
|
+
* @param {Object} input - the input that was recieved (optional).
|
|
942
|
+
* Can be a stringified Object.
|
|
943
|
+
*
|
|
944
|
+
* @return {Object} formatOutput - the query params as an object
|
|
945
|
+
*/
|
|
946
|
+
formatInputData(input) {
|
|
947
|
+
const origin = `${this.myid}-translatorUtil-formatInputData`;
|
|
948
|
+
log.trace(origin);
|
|
949
|
+
|
|
950
|
+
// prepare the input we received
|
|
951
|
+
let formatOutput = input;
|
|
952
|
+
|
|
953
|
+
// if the input was passed in as a string parse the json into an object
|
|
954
|
+
if (input && typeof input === 'string') {
|
|
955
|
+
try {
|
|
956
|
+
// parse the query parameters object that was passed in
|
|
957
|
+
formatOutput = JSON.parse(input);
|
|
958
|
+
} catch (err) {
|
|
959
|
+
log.trace(`${origin}: Could not JSON parse the input: ${err}`);
|
|
960
|
+
return input;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// return the object
|
|
965
|
+
return formatOutput;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* @summary Build a standard error object from the data provided
|
|
970
|
+
*
|
|
971
|
+
* @function formatErrorObject
|
|
972
|
+
* @param {String} origin - the originator of the error (optional).
|
|
973
|
+
* @param {String} type - the internal error type (optional).
|
|
974
|
+
* @param {Array} variables - the variables to put into the error message (optional).
|
|
975
|
+
* @param {Integer} sysCode - the error code from the other system (optional).
|
|
976
|
+
* @param {Object} sysRes - the raw response from the other system (optional).
|
|
977
|
+
* @param {Exception} stack - any available stack trace from the issue (optional).
|
|
978
|
+
*
|
|
979
|
+
* @return {Object} - the error object, null if missing pertinent information
|
|
980
|
+
*/
|
|
981
|
+
formatErrorObject(origin, type, variables, sysCode, sysRes, stack) {
|
|
982
|
+
log.trace(`${this.myid}-translatorUtil-formatErrorObject`);
|
|
983
|
+
|
|
984
|
+
// add the required fields
|
|
985
|
+
const errorObject = {
|
|
986
|
+
icode: 'AD.999',
|
|
987
|
+
IAPerror: {
|
|
988
|
+
origin: `${this.myid}-unidentified`,
|
|
989
|
+
displayString: 'error not provided',
|
|
990
|
+
recommendation: 'report this issue to the adapter team!'
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
if (origin) {
|
|
995
|
+
errorObject.IAPerror.origin = origin;
|
|
996
|
+
}
|
|
997
|
+
if (type) {
|
|
998
|
+
errorObject.IAPerror.displayString = type;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// add the messages from the error.json
|
|
1002
|
+
for (let e = 0; e < this.errors.length; e += 1) {
|
|
1003
|
+
if (this.errors[e].key === type) {
|
|
1004
|
+
errorObject.icode = this.errors[e].icode;
|
|
1005
|
+
errorObject.IAPerror.displayString = this.errors[e].displayString;
|
|
1006
|
+
errorObject.IAPerror.recommendation = this.errors[e].recommendation;
|
|
1007
|
+
} else if (this.errors[e].icode === type) {
|
|
1008
|
+
errorObject.icode = this.errors[e].icode;
|
|
1009
|
+
errorObject.IAPerror.displayString = this.errors[e].displayString;
|
|
1010
|
+
errorObject.IAPerror.recommendation = this.errors[e].recommendation;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// replace the variables
|
|
1015
|
+
let varCnt = 0;
|
|
1016
|
+
while (errorObject.IAPerror.displayString.indexOf('$VARIABLE$') >= 0) {
|
|
1017
|
+
let curVar = '';
|
|
1018
|
+
|
|
1019
|
+
// get the current variable
|
|
1020
|
+
if (variables && Array.isArray(variables) && variables.length >= varCnt + 1) {
|
|
1021
|
+
curVar = variables[varCnt];
|
|
1022
|
+
}
|
|
1023
|
+
varCnt += 1;
|
|
1024
|
+
errorObject.IAPerror.displayString = errorObject.IAPerror.displayString.replace('$VARIABLE$', curVar);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// add all of the optional fields
|
|
1028
|
+
if (sysCode) {
|
|
1029
|
+
errorObject.IAPerror.code = sysCode;
|
|
1030
|
+
}
|
|
1031
|
+
if (sysRes) {
|
|
1032
|
+
errorObject.IAPerror.raw_response = sysRes;
|
|
1033
|
+
}
|
|
1034
|
+
if (stack) {
|
|
1035
|
+
errorObject.IAPerror.stack = stack;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// return the object
|
|
1039
|
+
return errorObject;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* @summary Checks the type of error (internal v external). Then it either returns an
|
|
1044
|
+
* Exception object and returns the error object
|
|
1045
|
+
*
|
|
1046
|
+
* @function checkAndReturn
|
|
1047
|
+
* @param {Object} e - the exception to check.
|
|
1048
|
+
* @param {String} caller - the method that is calling this.
|
|
1049
|
+
* @param {String} logMsg - the message to be logged.
|
|
1050
|
+
*
|
|
1051
|
+
* @return - the error object
|
|
1052
|
+
*/
|
|
1053
|
+
checkAndReturn(e, caller, logMsg) {
|
|
1054
|
+
log.trace(`${this.myid}-translatorUtil-checkAndReturn`);
|
|
1055
|
+
let internal = null;
|
|
1056
|
+
let origin = caller;
|
|
1057
|
+
|
|
1058
|
+
if (!caller) {
|
|
1059
|
+
origin = `${this.myid}-unidentified`;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// create the generic part of an error object
|
|
1063
|
+
const errorObj = {
|
|
1064
|
+
origin,
|
|
1065
|
+
type: 'Caught Exception',
|
|
1066
|
+
vars: [],
|
|
1067
|
+
syscode: null,
|
|
1068
|
+
sysresp: null,
|
|
1069
|
+
exception: e
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
// determine if we already had an internal message
|
|
1073
|
+
try {
|
|
1074
|
+
internal = JSON.parse(e.message);
|
|
1075
|
+
} catch (ex) {
|
|
1076
|
+
// message was not internal
|
|
1077
|
+
log.error(`${origin}: ${logMsg}: ${e}`);
|
|
1078
|
+
internal = null;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// return the appropriate error message
|
|
1082
|
+
if (internal && internal.origin && internal.type) {
|
|
1083
|
+
return this.formatErrorObject(internal.origin, internal.type, internal.vars, internal.syscode, internal.sysresp, internal.exception);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
return this.formatErrorObject(errorObj.origin, errorObj.type, errorObj.vars, errorObj.syscode, errorObj.sysresp, errorObj.exception);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* @summary Checks the type of error (internal v external). Then it either rethrows it or
|
|
1091
|
+
* creates an Exception object and throws that
|
|
1092
|
+
*
|
|
1093
|
+
* @function checkAndThrow
|
|
1094
|
+
* @param {Object} e - the exception to check.
|
|
1095
|
+
* @param {String} caller - the method that is calling this.
|
|
1096
|
+
* @param {String} logMsg - the message to be logged.
|
|
1097
|
+
*
|
|
1098
|
+
* @return - throws a new Error
|
|
1099
|
+
*/
|
|
1100
|
+
checkAndThrow(e, caller, logMsg) {
|
|
1101
|
+
log.trace(`${this.myid}-translatorUtil-checkAndThrow`);
|
|
1102
|
+
let internal = null;
|
|
1103
|
+
let origin = caller;
|
|
1104
|
+
|
|
1105
|
+
if (!caller) {
|
|
1106
|
+
origin = `${this.myid}-unidentified`;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// create the generic part of an error object
|
|
1110
|
+
const errorObj = {
|
|
1111
|
+
origin,
|
|
1112
|
+
type: 'Caught Exception',
|
|
1113
|
+
vars: [],
|
|
1114
|
+
syscode: null,
|
|
1115
|
+
sysresp: null,
|
|
1116
|
+
exception: e
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
// determine if we already had an internal message
|
|
1120
|
+
try {
|
|
1121
|
+
internal = JSON.parse(e.message);
|
|
1122
|
+
} catch (ex) {
|
|
1123
|
+
// message was not internal
|
|
1124
|
+
log.error(`${origin}: ${logMsg}: ${e}`);
|
|
1125
|
+
internal = null;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// return the appropriate error message
|
|
1129
|
+
if (internal && internal.origin && internal.type) {
|
|
1130
|
+
throw e;
|
|
1131
|
+
} else {
|
|
1132
|
+
throw new Error(JSON.stringify(errorObj));
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
module.exports = AdapterTranslatorUtil;
|