@itentialopensource/adapter-utils 4.49.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +2 -0
- package/lib/brokerHandler.js +730 -0
- package/lib/cacheHandler.js +968 -0
- package/lib/connectorRest.js +28 -13
- package/lib/dbUtil.js +0 -3
- package/lib/genericHandler.js +280 -0
- package/lib/propertyUtil.js +134 -3
- package/lib/requestHandler.js +683 -209
- package/lib/translatorUtil.js +5 -4
- package/package.json +26 -26
- package/refs?service=git-upload-pack +0 -0
- package/schemas/propertiesSchema.json +576 -1
|
@@ -0,0 +1,968 @@
|
|
|
1
|
+
/* @copyright Itential, LLC 2023 */
|
|
2
|
+
|
|
3
|
+
// Set globals
|
|
4
|
+
/* global log */
|
|
5
|
+
/* eslint global-require:warn */
|
|
6
|
+
/* eslint import/no-dynamic-require: warn */
|
|
7
|
+
|
|
8
|
+
const AsyncLockCl = require('async-lock');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const jsonQuery = require('json-query');
|
|
11
|
+
|
|
12
|
+
const lock = new AsyncLockCl();
|
|
13
|
+
let id = null;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @summary Creates a Cache Entity object to organize the cache by entity type.
|
|
17
|
+
* @function createCacheEntity
|
|
18
|
+
* @param {String} entityName - type of entity
|
|
19
|
+
* @param {Array} entityList - array of data
|
|
20
|
+
* @param {Object} interval - id of interval
|
|
21
|
+
* @returns {Object} - created Cache Entity object
|
|
22
|
+
*/
|
|
23
|
+
function createCacheEntity(entityName, entityList, interval, sortEntities = true) {
|
|
24
|
+
const origin = `${id}-cacheHandler-createCacheEntity`;
|
|
25
|
+
log.trace(origin);
|
|
26
|
+
return ({
|
|
27
|
+
lockKey: entityName,
|
|
28
|
+
entityType: entityName,
|
|
29
|
+
list: entityList,
|
|
30
|
+
intervalId: interval,
|
|
31
|
+
sort: sortEntities
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @summary Generates name for data object. Currently non-unique
|
|
37
|
+
* @function generateName
|
|
38
|
+
* @returns {String} - generated name
|
|
39
|
+
*/
|
|
40
|
+
function generateName() {
|
|
41
|
+
log.warn('Name for entity not found, generated name placeholder');
|
|
42
|
+
return 'GeneratedName';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @summary Deletes cache data and properties. Data deletion
|
|
47
|
+
* saves considerable memory.
|
|
48
|
+
* @function deleteCacheData
|
|
49
|
+
* @param {Object} cacheHandler - this class instance
|
|
50
|
+
*/
|
|
51
|
+
function deleteCacheData(cacheHandler) {
|
|
52
|
+
const origin = `${id}-cacheHandler-deleteCacheData`;
|
|
53
|
+
log.trace(origin);
|
|
54
|
+
|
|
55
|
+
const handler = cacheHandler;
|
|
56
|
+
|
|
57
|
+
// get all keys to make sure nothing else is editing the cache at this time
|
|
58
|
+
const keysArr = [];
|
|
59
|
+
handler.cache.forEach((entity) => {
|
|
60
|
+
keysArr.push(entity.lockKey);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
lock.acquire(keysArr, () => {
|
|
64
|
+
if (!handler.enabled) {
|
|
65
|
+
handler.cache.forEach((entity) => {
|
|
66
|
+
clearInterval(entity.intervalId);
|
|
67
|
+
});
|
|
68
|
+
handler.cache = [];
|
|
69
|
+
handler.propertiesMap.clear();
|
|
70
|
+
log.warn(`${origin}: Cache deletion confirmed`);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @summary changes the frequency of which to update an entity type
|
|
77
|
+
* @function changeUpdateFrequency
|
|
78
|
+
* @param {Array of Objects} currentCache - cache
|
|
79
|
+
* @param {String} entityType - entity type of which to update
|
|
80
|
+
* @param {Integer} newFrequency - how often to updaate (in minutes)
|
|
81
|
+
* @param {cacheHandler} cacheHandler - instance of class to edit
|
|
82
|
+
*/
|
|
83
|
+
function changeUpdateFrequency(currentCache, entityType, newFrequency, cacheHandler) {
|
|
84
|
+
const origin = `${id}-cacheHandler-changeUpdateFrequency`;
|
|
85
|
+
log.trace(origin);
|
|
86
|
+
|
|
87
|
+
const cache = currentCache;
|
|
88
|
+
for (let i = 0; i < cache.length; i += 1) {
|
|
89
|
+
if (cache[i].entityType === entityType) {
|
|
90
|
+
cacheHandler.populateCache(entityType);
|
|
91
|
+
clearInterval(cache[i].intervalId);
|
|
92
|
+
cache[i].intervalId = setInterval(() => {
|
|
93
|
+
cacheHandler.populateCache(entityType);
|
|
94
|
+
}, newFrequency * 60000);
|
|
95
|
+
log.info(`Changed update frequency for ${entityType}.`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
log.error(`Failed to change interval. ${entityType} does not exist in cache.`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @summary Given an array of populate objects from a single entityType, initialize missing fields.
|
|
104
|
+
* @function validatePopulate
|
|
105
|
+
* @param {Array} populateArr - array of populate objects from properties
|
|
106
|
+
*/
|
|
107
|
+
function validatePopulate(populateArr) {
|
|
108
|
+
const origin = `${id}-cacheHandler-validatePopulate`;
|
|
109
|
+
log.trace(origin);
|
|
110
|
+
|
|
111
|
+
const arr = populateArr;
|
|
112
|
+
for (let i = 0; i < arr.length; i += 1) {
|
|
113
|
+
if (!arr[i].path) {
|
|
114
|
+
log.error(`No path in populate in properties for cache! Path is ${path}`);
|
|
115
|
+
// let brokerHandler handle this
|
|
116
|
+
} else {
|
|
117
|
+
arr[i].method = arr[i].method || 'GET';
|
|
118
|
+
arr[i].query = arr[i].query || {};
|
|
119
|
+
arr[i].body = arr[i].body || {};
|
|
120
|
+
arr[i].headers = arr[i].headers || {};
|
|
121
|
+
arr[i].handleFailure = arr[i].handleFailure || 'ignore';
|
|
122
|
+
arr[i].requestFields = arr[i].requestFields || {};
|
|
123
|
+
arr[i].responseDatakey = arr[i].responseDatakey || '';
|
|
124
|
+
arr[i].responseFields = arr[i].responseFields || {};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @summary Checks the properties passed in for the cache are valid,
|
|
131
|
+
* giving them default values if possible and removing them otherwise.
|
|
132
|
+
* @function validateProperties
|
|
133
|
+
* @param {Object} obj - properties json object
|
|
134
|
+
* @returns - adjusted valid properties with defaults
|
|
135
|
+
*/
|
|
136
|
+
function validateProperties(obj) {
|
|
137
|
+
const origin = `${id}-cacheHandler-validateProperties`;
|
|
138
|
+
log.trace(origin);
|
|
139
|
+
|
|
140
|
+
if (obj === null) {
|
|
141
|
+
log.error(`${origin}: Null properties!`);
|
|
142
|
+
throw new Error('Null properties!');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const props = obj;
|
|
146
|
+
// check cache, cache.enabled, cache.entities exist
|
|
147
|
+
if (!props.cache) {
|
|
148
|
+
// no cache, no props
|
|
149
|
+
props.cache = {
|
|
150
|
+
enabled: false,
|
|
151
|
+
entities: []
|
|
152
|
+
};
|
|
153
|
+
} else {
|
|
154
|
+
if (!props.cache.enabled) {
|
|
155
|
+
props.cache.enabled = false;
|
|
156
|
+
log.error(`${origin}: Passed in cache properties but did not specify enabled!`);
|
|
157
|
+
}
|
|
158
|
+
if (!props.cache.entities) {
|
|
159
|
+
props.cache.entities = [];
|
|
160
|
+
log.error(`${origin}: Passed in cache properties but did not specify entities (not even as empty array)!`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// check each entity type in entities
|
|
164
|
+
for (let i = 0; i < props.cache.entities.length; i += 1) {
|
|
165
|
+
// must have entityType and populate
|
|
166
|
+
if (!props.cache.entities[i].entityType || !props.cache.entities[i].populate || props.cache.entities[i].populate.length === 0) {
|
|
167
|
+
log.warn(`${origin}: removed invalid cache property at index ${i}`);
|
|
168
|
+
// remove rather than throw error
|
|
169
|
+
props.cache.entities.splice(i, 1);
|
|
170
|
+
i -= 1; // already pointing at next after splice
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// default frequency = 24 hrs, caps if <60 or >10080
|
|
175
|
+
if (!props.cache.entities[i].frequency) {
|
|
176
|
+
props.cache.entities[i].frequency = 24 * 60; // 1 day in minutes
|
|
177
|
+
} else if (props.cache.entities[i].frequency < 15) { // 15 min minimum
|
|
178
|
+
log.warn(`${origin}: Passed in frequency of ${props.cache.entities[i].frequency} is too small! Set to 15 minute.`);
|
|
179
|
+
props.cache.entities[i].frequency = 15;
|
|
180
|
+
} else if (props.cache.entities[i].frequency > 10080) { // 1 week max
|
|
181
|
+
log.warn(`${origin}: Passed in frequency of ${props.cache.entities[i].frequency} is too large! Set to 1 week.`);
|
|
182
|
+
props.cache.entities[i].frequency = 10080;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// default flushOnFail = false
|
|
186
|
+
if (!props.cache.entities[i].flushOnFail) {
|
|
187
|
+
props.cache.entities[i].flushOnFail = false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Limits entity type to 1000 entities
|
|
191
|
+
if (!props.cache.entities[i].limit) {
|
|
192
|
+
props.cache.entities[i].limits = 1000;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!props.cache.entities[i].retryAttempts) {
|
|
196
|
+
props.cache.entities[i].retryAttempts = 5; // currently only used for startup
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// make sure task and filterField exists for every object in cachedTasks
|
|
200
|
+
if (!props.cache.entities[i].cachedTasks) {
|
|
201
|
+
props.cache.entities[i].cachedTasks = [];
|
|
202
|
+
} else {
|
|
203
|
+
props.cache.entities[i].cachedTasks.forEach((cacheTask) => {
|
|
204
|
+
const curTask = cacheTask; // for lint
|
|
205
|
+
if (!curTask.name) {
|
|
206
|
+
log.error('Cached task has no task name!');
|
|
207
|
+
curTask.name = ''; // prevent errors, will not ever match a task name
|
|
208
|
+
}
|
|
209
|
+
if (!curTask.filterField) {
|
|
210
|
+
// if no filter then assume is a get all call, would need to explicitly check this
|
|
211
|
+
curTask.filterField = '';
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// fill in missing populate fields
|
|
217
|
+
validatePopulate(props.cache.entities[i].populate);
|
|
218
|
+
}
|
|
219
|
+
return props;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @summary Compares two objects by name for sorting purposes.
|
|
224
|
+
* @function compareByName
|
|
225
|
+
* @param {Object} a - object to compare
|
|
226
|
+
* @param {Object} b - object to compare
|
|
227
|
+
* @returns {int} - specifying data order in relation to each other
|
|
228
|
+
*/
|
|
229
|
+
function compareByName(a, b) {
|
|
230
|
+
if (a.name > b.name) {
|
|
231
|
+
return 1;
|
|
232
|
+
}
|
|
233
|
+
if (a.name < b.name) {
|
|
234
|
+
return -1;
|
|
235
|
+
}
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @summary tests if object is an options object. Defaults:
|
|
241
|
+
* - filter = ""
|
|
242
|
+
* - start = 0 and limit = MAX_SAFE_INTEGER
|
|
243
|
+
* - sort = false
|
|
244
|
+
* @function validateOptions
|
|
245
|
+
* @param {Object} obj - object to be tested
|
|
246
|
+
* @return {Object} - modified object with defaults
|
|
247
|
+
*/
|
|
248
|
+
function validateOptions(obj) {
|
|
249
|
+
const origin = `${id}-cacheHandler-validateOptions`;
|
|
250
|
+
log.trace(origin);
|
|
251
|
+
|
|
252
|
+
const opt = obj || {};
|
|
253
|
+
|
|
254
|
+
const keys = Object.keys(opt);
|
|
255
|
+
if (!keys.includes('filter')) {
|
|
256
|
+
opt.filter = {}; // filter for nothing means get everything
|
|
257
|
+
}
|
|
258
|
+
if (!keys.includes('start') && !keys.includes('limit')) {
|
|
259
|
+
opt.start = 0;
|
|
260
|
+
opt.limit = Number.MAX_SAFE_INTEGER; // return everything, maxed at max safe integer
|
|
261
|
+
} else if (!(keys.includes('start') && keys.includes('limit')) || opt.start < 0 || opt.limit < 0) {
|
|
262
|
+
log.error(`${origin}: incomplete or invalid options with start and limit!`);
|
|
263
|
+
throw new Error('Invalid options on accessing cache');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return opt;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @summary Helper method that retrieves entities from the cache
|
|
271
|
+
* @function retrieveCacheEntriesHelper
|
|
272
|
+
*
|
|
273
|
+
* @param {Object} cache - cache to search through
|
|
274
|
+
* @param {String} entityType - entity type to retrieve
|
|
275
|
+
* @param {Object} options - options on how to filter and return the data
|
|
276
|
+
*/
|
|
277
|
+
function retrieveCacheEntriesHelper(cache, entityType, options, callback) {
|
|
278
|
+
// go through the cache to find the entity we care about
|
|
279
|
+
for (let i = 0; i < cache.length; i += 1) {
|
|
280
|
+
// check if entity type we care about
|
|
281
|
+
if (cache[i].entityType === entityType) {
|
|
282
|
+
// Lock the cache for the entity so we can take what is in the cache
|
|
283
|
+
// Lock and instant unlock as wait until lock is free
|
|
284
|
+
return lock.acquire(cache[i].lockKey, (done) => {
|
|
285
|
+
// return the entity's list and free the lock
|
|
286
|
+
done(cache[i].list);
|
|
287
|
+
}, (ret) => {
|
|
288
|
+
let arr = [];
|
|
289
|
+
|
|
290
|
+
// populate re-attempt fails
|
|
291
|
+
if (!ret) {
|
|
292
|
+
log.error(`${origin}: No list in retrieve helper, re-attempt populate failed!`);
|
|
293
|
+
return callback(null, 'No list in retrieve cache helper!');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// no cache data found
|
|
297
|
+
if (ret.length === 0) {
|
|
298
|
+
log.info('Cache data retrieved and found to be empty.');
|
|
299
|
+
return callback(arr);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// apply filter
|
|
303
|
+
if (!options.filter || Object.keys(options.filter).length === 0) {
|
|
304
|
+
// no filter, get everything
|
|
305
|
+
log.info('No filter applied to cache retrieved');
|
|
306
|
+
arr = ret;
|
|
307
|
+
} else {
|
|
308
|
+
// get cache data with substring in name
|
|
309
|
+
const filterField = Object.keys(options.filter)[0];
|
|
310
|
+
const filter = options.filter[filterField];
|
|
311
|
+
for (let j = 0; j < ret.length; j += 1) {
|
|
312
|
+
if (ret[j][filterField].includes(filter)) {
|
|
313
|
+
arr.push(ret[j]);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// don't need to sort if we know it's OOB
|
|
319
|
+
if (options.limit * options.start >= arr.length) {
|
|
320
|
+
log.warn(`${options.start} is not a valid page, too large!`);
|
|
321
|
+
return callback([]);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// pagination: get entities limit*start to limit*(start+1) - 1
|
|
325
|
+
if (options.start >= 0 && options.limit > 0) { // Probably should handle this in properties.
|
|
326
|
+
// if last "page" then will be returning fewer items
|
|
327
|
+
const end = Math.min(arr.length, options.limit * (options.start + 1));
|
|
328
|
+
arr = arr.slice(options.limit * options.start, end);
|
|
329
|
+
}
|
|
330
|
+
return callback(arr); // for linting
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
log.error(`${origin}: No entityType ${entityType} in retrieve helper!`);
|
|
335
|
+
return callback(null, `No ${entityType} in retrieve cache helper!`); // error not found
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @summary Removes all data of a certain entity type
|
|
340
|
+
* @function removeCacheEntry
|
|
341
|
+
* @param {Array} cache - the cache to remove from
|
|
342
|
+
* @param {String} entityType - the entity type of which to remove
|
|
343
|
+
*/
|
|
344
|
+
function removeCacheEntry(cache, entityType) {
|
|
345
|
+
const origin = `${id}-cacheHandler-removeCacheEntry`;
|
|
346
|
+
log.trace(origin);
|
|
347
|
+
|
|
348
|
+
for (let i = 0; i < cache.length; i += 1) {
|
|
349
|
+
if (cache[i].entityType === entityType) {
|
|
350
|
+
lock.acquire(cache[i].lockKey, () => {
|
|
351
|
+
clearInterval(cache[i].intervalId);
|
|
352
|
+
cache[i].splice(i, 1);
|
|
353
|
+
});
|
|
354
|
+
log.info(`Successfully removed ${entityType} from cache.`);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
log.error(`${origin}: Did not find cache type ${entityType} to remove!`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @function getDataFromSources
|
|
363
|
+
* @summary INTERNAL FUNCTION: get data from source(s) - nested
|
|
364
|
+
* @param {*} loopField - fields
|
|
365
|
+
* @param {Array} sources - sources to look into
|
|
366
|
+
* @returns {*} - nested field value
|
|
367
|
+
*/
|
|
368
|
+
function getDataFromSources(loopField, sources) {
|
|
369
|
+
let fieldValue = loopField;
|
|
370
|
+
|
|
371
|
+
// go through the sources to find the field
|
|
372
|
+
for (let s = 0; s < sources.length; s += 1) {
|
|
373
|
+
// find the field value using jsonquery
|
|
374
|
+
const nestedValue = jsonQuery(loopField, { data: sources[s] }).value;
|
|
375
|
+
|
|
376
|
+
// if we found in source - set and no need to check other sources
|
|
377
|
+
if (nestedValue) {
|
|
378
|
+
fieldValue = nestedValue;
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return fieldValue;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* @summary Manipulates data from call to only represent the specified fields and id and name if they do not have one
|
|
388
|
+
*
|
|
389
|
+
* @function parseResponseFields
|
|
390
|
+
* @param {Boolean} responseFull - whether or not to take in all fields
|
|
391
|
+
* @param {Object of output to call result} responseFields - fields to take in
|
|
392
|
+
* - ie response field of "name": "id" will give the iap call output a name field
|
|
393
|
+
* with the value being the id
|
|
394
|
+
* @param {Array of Objects} allData - data returned from IAP Call
|
|
395
|
+
* @return {Array of Objects} - modified data
|
|
396
|
+
*/
|
|
397
|
+
function parseResponseFields(responseFields, allData, end, requestFields) {
|
|
398
|
+
const origin = `${id}-cacheHandler-createCacheData`;
|
|
399
|
+
log.trace(origin);
|
|
400
|
+
|
|
401
|
+
const parsedData = [];
|
|
402
|
+
const rfKeys = Object.keys(responseFields);
|
|
403
|
+
log.debug(rfKeys);
|
|
404
|
+
|
|
405
|
+
let ostypePrefix = '';
|
|
406
|
+
if (responseFields.ostypePrefix) {
|
|
407
|
+
ostypePrefix = responseFields.ostypePrefix;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
let statusValue = true;
|
|
411
|
+
if (responseFields.statusValue) {
|
|
412
|
+
statusValue = responseFields.statusValue;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
allData.response.forEach((currData) => {
|
|
416
|
+
const newObj = currData;
|
|
417
|
+
|
|
418
|
+
for (let rf = 0; rf < rfKeys.length; rf += 1) {
|
|
419
|
+
if (rfKeys[rf] !== 'ostypePrefix') {
|
|
420
|
+
let fieldValue = getDataFromSources(responseFields[rfKeys[rf]], [currData, { fake: 'fakedata' }, requestFields]);
|
|
421
|
+
|
|
422
|
+
// if the field is ostype - need to add prefix
|
|
423
|
+
if (rfKeys[rf] === 'ostype' && typeof fieldValue === 'string') {
|
|
424
|
+
fieldValue = ostypePrefix + fieldValue;
|
|
425
|
+
}
|
|
426
|
+
// if there is a status to set, set it
|
|
427
|
+
if (rfKeys[rf] === 'status') {
|
|
428
|
+
// if really looking for just a good response
|
|
429
|
+
if (responseFields[rfKeys[rf]] === 'return2xx' && allData.icode === statusValue.toString()) {
|
|
430
|
+
newObj.isAlive = true;
|
|
431
|
+
} else if (fieldValue.toString() === statusValue.toString()) {
|
|
432
|
+
newObj.isAlive = true;
|
|
433
|
+
} else {
|
|
434
|
+
newObj.isAlive = false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// if we found a good value
|
|
438
|
+
newObj[rfKeys[rf]] = fieldValue;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (!Object.prototype.hasOwnProperty.call(currData, 'name')) {
|
|
443
|
+
newObj.name = generateName();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// no longer requiring id field
|
|
447
|
+
|
|
448
|
+
parsedData.push(newObj);
|
|
449
|
+
});
|
|
450
|
+
return parsedData;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* @summary makes IAP Calls through Generic Handler
|
|
455
|
+
*
|
|
456
|
+
* @function makeIAPCall
|
|
457
|
+
* @param calls - calls to make
|
|
458
|
+
* @param requestHandler - instance of requestHandler to make call
|
|
459
|
+
* @param callback - data or error
|
|
460
|
+
*/
|
|
461
|
+
function makeIAPCall(calls, requestHandler, callback) { // todo pass in properties from cacheHandler
|
|
462
|
+
const callPromises = [];
|
|
463
|
+
log.debug('Starting an iap call from cache.');
|
|
464
|
+
for (let i = 0; i < calls.length; i += 1) {
|
|
465
|
+
log.debug('Response :', calls[i].responseFields);
|
|
466
|
+
callPromises.push(new Promise((resolve, reject) => {
|
|
467
|
+
requestHandler.genericAdapterRequest(calls[i].path, calls[i].method, calls[i].query, calls[i].body, calls[i].headers, (callRet, callErr) => {
|
|
468
|
+
if (callErr) {
|
|
469
|
+
log.error('Make iap call failed with error');
|
|
470
|
+
log.error(callErr);
|
|
471
|
+
if (callErr.icode === 'AD.301') {
|
|
472
|
+
log.error(callErr.IAPerror.displayString);
|
|
473
|
+
return reject(callErr);
|
|
474
|
+
}
|
|
475
|
+
if (calls[i].handleFailure === 'ignore') {
|
|
476
|
+
log.info(`Call failed for path ${calls[i].path} with error code ${callErr.icode}. Ignoring.`);
|
|
477
|
+
return resolve([]);
|
|
478
|
+
}
|
|
479
|
+
log.warn(`Call failed for path ${calls[i].path} with error code ${callErr.icode}. Rejecting.`);
|
|
480
|
+
return reject(callErr);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// callRet is the object returned from that call
|
|
484
|
+
log.info(`Sucessful Call. Adding to cache from path ${calls[i].path}`);
|
|
485
|
+
const result = callRet;
|
|
486
|
+
if (calls[i].responseDatakey) {
|
|
487
|
+
result.response = jsonQuery(calls[i].responseDatakey, { data: result.response }).value;
|
|
488
|
+
}
|
|
489
|
+
return resolve(parseResponseFields(calls[i].responseFields, result, i, calls[i].requestFields));
|
|
490
|
+
});
|
|
491
|
+
}));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
let returnData = [];
|
|
495
|
+
// Reworking the brokerHandler calls
|
|
496
|
+
return Promise.all(callPromises).then((results) => { // array of each Promise result
|
|
497
|
+
for (let i = 0; i < results.length; i += 1) {
|
|
498
|
+
returnData = returnData.concat(results[i]);
|
|
499
|
+
}
|
|
500
|
+
callback(returnData, null); // Returns data if all calls succeed
|
|
501
|
+
}, (error) => {
|
|
502
|
+
callback(null, error); // Throws error if at least one call failed
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @summary Helper method that tries to populate the cache after 5 seconds
|
|
508
|
+
*
|
|
509
|
+
* @param {String} entityType - entity type to populate
|
|
510
|
+
* @param {CacheHandler} cacheHandler - instance of cacheHandler
|
|
511
|
+
* @param {Integer} attemptsRemaining - amount of attempts left to retry
|
|
512
|
+
* @param {function} callback - returns whether or not that cache was successfully populated
|
|
513
|
+
*/
|
|
514
|
+
function populateTimeout(entityType, cacheHandler, attemptsRemaining, callback) {
|
|
515
|
+
let ar = attemptsRemaining;
|
|
516
|
+
log.info(`Retrying to populate cache for ${entityType}. Attempts Remaining: ${ar}`);
|
|
517
|
+
setTimeout(() => {
|
|
518
|
+
cacheHandler.populateCache(entityType).then((result) => {
|
|
519
|
+
if (result && result[0] === 'success') {
|
|
520
|
+
return callback(true);
|
|
521
|
+
}
|
|
522
|
+
ar -= 1;
|
|
523
|
+
if (ar === 0) {
|
|
524
|
+
return callback(false);
|
|
525
|
+
}
|
|
526
|
+
return populateTimeout(entityType, cacheHandler, ar, callback);
|
|
527
|
+
});
|
|
528
|
+
}, 10000);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* @summary tries to populate the cache again after populate fails on startup
|
|
533
|
+
*
|
|
534
|
+
* @param {String} entityType - entity type to populate
|
|
535
|
+
* @param {CacheHandler} cacheHandler - instance of CacheHandler
|
|
536
|
+
* @param {Integer} attempts - amount of attempts to retry
|
|
537
|
+
*/
|
|
538
|
+
async function retryPopulate(entityType, cacheHandler, attempts) {
|
|
539
|
+
log.info('Retrying populate...');
|
|
540
|
+
return new Promise((resolve) => {
|
|
541
|
+
populateTimeout(entityType, cacheHandler, attempts, (success) => {
|
|
542
|
+
if (success) {
|
|
543
|
+
return resolve(`Cache updated for ${entityType}.`);
|
|
544
|
+
}
|
|
545
|
+
return resolve(`Populate cache for ${entityType} failed.`);
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Exposed handler class
|
|
551
|
+
class CacheHandler {
|
|
552
|
+
/**
|
|
553
|
+
* Adapter Cache Handler
|
|
554
|
+
* @constructor
|
|
555
|
+
*/
|
|
556
|
+
constructor(prongId, properties, directory, reqH) {
|
|
557
|
+
id = prongId; // accessable to non-exposed methods
|
|
558
|
+
this.baseDir = directory;
|
|
559
|
+
this.requestHandler = reqH; // Request Handler object with id, props, dir
|
|
560
|
+
|
|
561
|
+
this.cache = []; // array of CacheEntity objects
|
|
562
|
+
|
|
563
|
+
// set up the properties I care about
|
|
564
|
+
this.propertiesMap = new Map(); // entityType->properties map
|
|
565
|
+
this.refreshProperties(properties);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* refreshProperties is used to set up all of the properties for the cache handler.
|
|
570
|
+
* It allows properties to be changed later by simply calling refreshProperties rather
|
|
571
|
+
* than having to restart the cache handler.
|
|
572
|
+
*
|
|
573
|
+
* @function refreshProperties
|
|
574
|
+
* @param {Object} properties - an object containing all of the properties
|
|
575
|
+
*/
|
|
576
|
+
refreshProperties(properties) {
|
|
577
|
+
const origin = `${id}-cacheHandler-refreshProperties`;
|
|
578
|
+
log.trace(origin);
|
|
579
|
+
|
|
580
|
+
if (properties === null) {
|
|
581
|
+
log.error(`${origin}: Cache Handler received no properties!`);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
this.props = validateProperties(properties);
|
|
586
|
+
|
|
587
|
+
const wasEnabled = this.enabled || false; // in case undefined
|
|
588
|
+
this.enabled = this.props.cache.enabled;
|
|
589
|
+
if (!this.enabled) {
|
|
590
|
+
// destroy cache and properties for memory efficiency
|
|
591
|
+
if (wasEnabled) {
|
|
592
|
+
deleteCacheData(this);
|
|
593
|
+
}
|
|
594
|
+
log.warn('Cache was disabled from enabled, removed cache memory.');
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// check entityType was not deleted (if so flush that cache type)
|
|
599
|
+
this.propertiesMap.forEach((value, key) => {
|
|
600
|
+
for (let i = 0; i < this.props.cache.entities.length; i += 1) {
|
|
601
|
+
if (!this.props.cache.entities[i].name === key) { // currently in cache, but not props
|
|
602
|
+
removeCacheEntry(this.cache, key);
|
|
603
|
+
this.propertiesMap.delete(key);
|
|
604
|
+
log.warn(`${origin}: Removed cache entity type due to change in properties.`);
|
|
605
|
+
log.info(`Deleted props for entity type ${key} with val ${value}`);
|
|
606
|
+
return; // continue forEach
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// set properties map
|
|
612
|
+
this.props.cache.entities.forEach((entity) => {
|
|
613
|
+
if (!this.propertiesMap.has(entity.entityType)) {
|
|
614
|
+
// entity does not exist yet
|
|
615
|
+
this.propertiesMap.set(entity.entityType, {});
|
|
616
|
+
this.propertiesMap.get(entity.entityType).frequency = entity.frequency;
|
|
617
|
+
this.propertiesMap.get(entity.entityType).flushOnFail = entity.flushOnFail;
|
|
618
|
+
this.propertiesMap.get(entity.entityType).populate = entity.populate;
|
|
619
|
+
this.propertiesMap.get(entity.entityType).limit = entity.limit;
|
|
620
|
+
this.propertiesMap.get(entity.entityType).retryAttempts = entity.retryAttempts;
|
|
621
|
+
|
|
622
|
+
const newIntervalId = setInterval(() => {
|
|
623
|
+
this.populateCache(entity.entityType);
|
|
624
|
+
}, entity.frequency * 60000);
|
|
625
|
+
|
|
626
|
+
const sort = Object.prototype.hasOwnProperty.call(entity, 'sort') ? entity.sort : true; // default true
|
|
627
|
+
this.cache.push(createCacheEntity(entity.entityType, null, newIntervalId, sort));
|
|
628
|
+
this.populateCache(entity.entityType).then((result) => {
|
|
629
|
+
if (result && result[0] === 'error') {
|
|
630
|
+
retryPopulate(entity.entityType, this, entity.retryAttempts).then((nextResult) => {
|
|
631
|
+
log.info(nextResult);
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
}).catch((error) => {
|
|
635
|
+
log.error('Failed populate cache!');
|
|
636
|
+
if (error.icode === 'AD.301') {
|
|
637
|
+
log.error(`Destroying interval, check path and call in properties for ${entity.entityType}`);
|
|
638
|
+
clearInterval(newIntervalId);
|
|
639
|
+
this.cache.removeCacheEntry(this.cache, entity.entityType);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
} else {
|
|
643
|
+
// entity props frequency updated
|
|
644
|
+
if (entity.frequency !== this.propertiesMap.get(entity.entityType).frequency) {
|
|
645
|
+
changeUpdateFrequency(this.cache, entity.entityType, entity.frequency, this);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
this.propertiesMap.get(entity.entityType).frequency = entity.frequency;
|
|
649
|
+
this.propertiesMap.get(entity.entityType).flushOnFail = entity.flushOnFail;
|
|
650
|
+
this.propertiesMap.get(entity.entityType).populate = entity.populate;
|
|
651
|
+
this.propertiesMap.get(entity.entityType).limit = entity.limit;
|
|
652
|
+
this.propertiesMap.get(entity.entityType).retryAttempts = entity.retryAttempts;
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* @summary Populates/updates cache with entities' data from IAP call
|
|
659
|
+
* @function populateCache
|
|
660
|
+
* @param {String/Array of Strings} entities - entities of which we want to store
|
|
661
|
+
* @param {Boolean} onlyIfNecessary - If true and cache has been populated after acquiring the lock, does nothing
|
|
662
|
+
* @return {Array of Strings} - whether each entity succeeded or errored
|
|
663
|
+
*/
|
|
664
|
+
async populateCache(entities) {
|
|
665
|
+
const origin = `${id}-cacheHandler-populateCache`;
|
|
666
|
+
log.trace(origin);
|
|
667
|
+
|
|
668
|
+
if (!this.enabled) {
|
|
669
|
+
return Promise.reject(new Error('Cache is not enabled!'));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
log.info('Populating cache.');
|
|
673
|
+
|
|
674
|
+
// support string and array input
|
|
675
|
+
let entityArr = entities;
|
|
676
|
+
if (!Array.isArray(entities)) {
|
|
677
|
+
entityArr = [entities];
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// need all properties to make iap call
|
|
681
|
+
for (let i = 0; i < entityArr.length; i += 1) {
|
|
682
|
+
const entityType = entityArr[i];
|
|
683
|
+
if (!this.propertiesMap.has(entityType)) {
|
|
684
|
+
log.error(`${entityType} is an untracked entity type! Check properties.`);
|
|
685
|
+
return Promise.reject(new Error(`${entityType} is an untracked entity type! Check properties.`));
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
try {
|
|
690
|
+
const promiseArr = [];
|
|
691
|
+
// for each entityType need to populate
|
|
692
|
+
for (let j = 0; j < entityArr.length; j += 1) {
|
|
693
|
+
const entityType = entityArr[j];
|
|
694
|
+
// try to find current entityType in cache
|
|
695
|
+
for (let i = 0; i < this.cache.length; i += 1) {
|
|
696
|
+
if (this.cache[i].entityType === entityType) { // will always be found
|
|
697
|
+
promiseArr.push(new Promise((resolve, reject) => {
|
|
698
|
+
makeIAPCall(this.propertiesMap.get(entityType).populate, this.requestHandler, (cacheData, error) => {
|
|
699
|
+
// error message if applicable
|
|
700
|
+
let errorMsg = null;
|
|
701
|
+
if (error) {
|
|
702
|
+
errorMsg = `${origin}: ${entityType} failed with error ${error.IAPerror.displayString}.`;
|
|
703
|
+
} else if (cacheData.length > this.propertiesMap.get(entityType).limit) {
|
|
704
|
+
errorMsg = `${origin}: ${entityType} failed. Data surpassed the limit.`;
|
|
705
|
+
} else if (cacheData.length === 0) {
|
|
706
|
+
errorMsg = `${origin}: ${entityType} failed. Nothing found.`;
|
|
707
|
+
}
|
|
708
|
+
// handle error
|
|
709
|
+
if (errorMsg !== null) {
|
|
710
|
+
/**
|
|
711
|
+
* removed unnecessary check if cache[i].list is null here
|
|
712
|
+
* setting null var to null again is fine and shortens critical section
|
|
713
|
+
* */
|
|
714
|
+
|
|
715
|
+
if (this.propertiesMap.get(entityType).flushOnFail) {
|
|
716
|
+
lock.acquire(this.cache[i].lockKey, (done) => {
|
|
717
|
+
this.cache[i].list = null;
|
|
718
|
+
done(`${errorMsg} Flushed.`);
|
|
719
|
+
}, (ret) => {
|
|
720
|
+
log.error(ret);
|
|
721
|
+
});
|
|
722
|
+
} else {
|
|
723
|
+
log.error(`${errorMsg} Keeping old data.`);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// bad path
|
|
727
|
+
if (error && error.icode === 'AD.301') {
|
|
728
|
+
return reject(error);
|
|
729
|
+
}
|
|
730
|
+
return resolve('error');
|
|
731
|
+
}
|
|
732
|
+
// sort cache data, default true
|
|
733
|
+
if (this.cache[i].sort) {
|
|
734
|
+
cacheData.sort(compareByName);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// no error, edit cache
|
|
738
|
+
lock.acquire(this.cache[i].lockKey, (done) => {
|
|
739
|
+
if (this.enabled) { // in case properties changes during iap call
|
|
740
|
+
this.cache[i].list = cacheData;
|
|
741
|
+
done(`${entityType}: cache updated.`);
|
|
742
|
+
} else {
|
|
743
|
+
done('Populate cancelled due to disabled cache!');
|
|
744
|
+
}
|
|
745
|
+
}, (ret) => {
|
|
746
|
+
log.info(ret);
|
|
747
|
+
});
|
|
748
|
+
return resolve('success');
|
|
749
|
+
});
|
|
750
|
+
}));
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const arr = await Promise.allSettled(promiseArr);
|
|
756
|
+
return new Promise((resolve, reject) => {
|
|
757
|
+
const valueArray = [];
|
|
758
|
+
for (let p = 0; p < arr.length; p += 1) {
|
|
759
|
+
if (arr[p].status === 'rejected') {
|
|
760
|
+
reject(arr[p].reason);
|
|
761
|
+
}
|
|
762
|
+
valueArray.push(arr[p].value);
|
|
763
|
+
}
|
|
764
|
+
resolve(valueArray);
|
|
765
|
+
});
|
|
766
|
+
} catch (e) {
|
|
767
|
+
// re-throw any exception
|
|
768
|
+
throw new Error(e);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* @summary Retrieves an entity's data from cache under specified filters
|
|
774
|
+
* @function retrieveCacheEntries
|
|
775
|
+
* @param {String} entityType - entity type that we want to retrieve items from
|
|
776
|
+
* @param {Object} opts - Options of what we want to search for and return
|
|
777
|
+
* Keys:
|
|
778
|
+
* Filter (String) - Substring in which to see which's data
|
|
779
|
+
* names contain
|
|
780
|
+
* Start (int) - Pagination Number (Not sure how to implement)
|
|
781
|
+
* Limit (int) - limit of how much data to send back
|
|
782
|
+
* Sort (Boolean) - whether we sort the data or not
|
|
783
|
+
* @return {Array} - array of data objects
|
|
784
|
+
*/
|
|
785
|
+
retrieveCacheEntries(entityType, opts, callback) {
|
|
786
|
+
const origin = `${id}-cacheHandler-retrieveCacheEntries`;
|
|
787
|
+
log.trace(origin);
|
|
788
|
+
|
|
789
|
+
if (!this.enabled) {
|
|
790
|
+
log.warn(`${origin}: Cache is not enabled!`);
|
|
791
|
+
return callback(null, 'Cache is not enabled.');
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (typeof entityType !== 'string') {
|
|
795
|
+
log.error(`${entityType} is not of type String`);
|
|
796
|
+
return callback(null, `${entityType} is not of type String`);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
try {
|
|
800
|
+
const options = validateOptions(opts); // may throw error
|
|
801
|
+
// go through the cache to find the entity we care about
|
|
802
|
+
for (let i = 0; i < this.cache.length; i += 1) {
|
|
803
|
+
// check if entity type we care about
|
|
804
|
+
if (this.cache[i].entityType === entityType) {
|
|
805
|
+
// Lock the cache for the entity so we can take what is in the cache
|
|
806
|
+
// Lock and instant unlock as wait until lock is free
|
|
807
|
+
return lock.acquire(this.cache[i].lockKey, (done) => {
|
|
808
|
+
// return the entity's list and free the lock
|
|
809
|
+
done(this.cache[i].list);
|
|
810
|
+
}, (ret) => {
|
|
811
|
+
// last iap call failed and flushed, attempt new iap call
|
|
812
|
+
if (!ret) {
|
|
813
|
+
// this returns a promise but below we return a callback...
|
|
814
|
+
return this.populateCache(entityType).then((result) => {
|
|
815
|
+
if (result && result[0] === 'success') {
|
|
816
|
+
return retrieveCacheEntriesHelper(this.cache, entityType, options, callback);
|
|
817
|
+
}
|
|
818
|
+
return callback(null, `Retrieve call for ${entityType} failed, and populate re-attempt failed`);
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// normal retrieve
|
|
823
|
+
return retrieveCacheEntriesHelper(this.cache, entityType, options, callback);
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
log.error(`${origin}: Failed to find ${entityType} in retrieve!`);
|
|
828
|
+
return callback(null, `Retrieve call for ${entityType} failed.`);
|
|
829
|
+
} catch (e) {
|
|
830
|
+
// re-throw any exception
|
|
831
|
+
throw new Error(e);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* @summary Method to check if the entity is in the cache. Faster than
|
|
837
|
+
* retrieveCacheEntries hence the seperation
|
|
838
|
+
*
|
|
839
|
+
* @function isEntityCached
|
|
840
|
+
* @param {String} entityType - the entity type to check for
|
|
841
|
+
* @param {Array of String} entityNames - the specific entities we are looking for of that type
|
|
842
|
+
*
|
|
843
|
+
* @return {Array of Boolean} - whether associated entity is found
|
|
844
|
+
*/
|
|
845
|
+
isEntityCached(entityType, entityNames, callback) {
|
|
846
|
+
const origin = `${id}-cacheHandler-isEntityCached`;
|
|
847
|
+
log.trace(origin);
|
|
848
|
+
// Possibly supporting checking if entityType is cache? (set entityId = null)
|
|
849
|
+
if (!this.enabled) {
|
|
850
|
+
log.warn(`${origin}: Cache is not enabled!`);
|
|
851
|
+
return callback(null, 'Cache is not enabled!');
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
let names = entityNames;
|
|
855
|
+
if (!Array.isArray(entityNames)) {
|
|
856
|
+
names = [entityNames];
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
try {
|
|
860
|
+
for (let i = 0; i < this.cache.length; i += 1) {
|
|
861
|
+
if (this.cache[i].entityType === entityType) {
|
|
862
|
+
const returnVals = [];
|
|
863
|
+
return lock.acquire(this.cache[i].lockKey, (done) => {
|
|
864
|
+
done(this.cache[i].list);
|
|
865
|
+
}, (ret) => {
|
|
866
|
+
// null list means previously flushed or failed called
|
|
867
|
+
if (!ret) {
|
|
868
|
+
log.info(`Did not find ${entityType}, attempting populate in isEntityCached`);
|
|
869
|
+
return this.populateCache(entityType).then((result) => { // populate will re-acquire lock
|
|
870
|
+
if (result && result[0] === 'success') {
|
|
871
|
+
// re-acquire lock to read list
|
|
872
|
+
lock.acquire(this.cache[i].lockKey, (done) => {
|
|
873
|
+
names.forEach((name) => {
|
|
874
|
+
for (let j = 0; j < this.cache[i].list.length; j += 1) {
|
|
875
|
+
if (this.cache[i].list[j].name === name) {
|
|
876
|
+
returnVals.push('found');
|
|
877
|
+
return; // forEach
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
returnVals.push('notfound');
|
|
881
|
+
});
|
|
882
|
+
done(returnVals);
|
|
883
|
+
}, (retVals) => {
|
|
884
|
+
log.info(retVals);
|
|
885
|
+
return callback(retVals);
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
return callback(null, `isEntityCached call for ${entityType} failed.`);
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
names.forEach((curName) => {
|
|
893
|
+
for (let j = 0; j < ret.length; j += 1) {
|
|
894
|
+
if (ret[j].name === curName) {
|
|
895
|
+
returnVals.push('found');
|
|
896
|
+
return; // continue the forEach
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
returnVals.push('notfound');
|
|
900
|
+
});
|
|
901
|
+
log.info(returnVals);
|
|
902
|
+
return callback(returnVals);
|
|
903
|
+
}); // end of lock acquire
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
log.error(`${entityType} not found in cache.`);
|
|
907
|
+
return callback(false);
|
|
908
|
+
} catch (e) {
|
|
909
|
+
log.error(e);
|
|
910
|
+
return callback(false);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* @function isEntityTypeToBeCached
|
|
916
|
+
* @summary Checks whether the cache properties for that entity type
|
|
917
|
+
* is set up, meaning that the entity type should be cached
|
|
918
|
+
* @param {String} entityType - the entity type to check for
|
|
919
|
+
* @returns {Boolean} - whether the entity type is set to be cached
|
|
920
|
+
*/
|
|
921
|
+
isEntityTypeToBeCached(entityType, callback) {
|
|
922
|
+
const origin = `${id}-cacheHandler-isEntityTypeToBeCached`;
|
|
923
|
+
log.trace(origin);
|
|
924
|
+
|
|
925
|
+
if (!this.enabled) {
|
|
926
|
+
log.error('Cache is not enabled!');
|
|
927
|
+
return callback(null, 'Cache is not enabled!');
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
return callback(this.propertiesMap.has(entityType));
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* @function isTaskCached
|
|
935
|
+
* @summary Checks whether the task should be cached, for adapter
|
|
936
|
+
* (non-broker level) calls
|
|
937
|
+
* @param {String} entityType - entity type from identifyRequest
|
|
938
|
+
* @param {String} task - method name
|
|
939
|
+
*/
|
|
940
|
+
isTaskCached(entityType, task) {
|
|
941
|
+
const origin = `${id}-cacheHandler-isTaskCached`;
|
|
942
|
+
log.trace(origin);
|
|
943
|
+
|
|
944
|
+
// do not bother with callbacks here
|
|
945
|
+
if (!this.enabled || !this.propertiesMap.has(entityType)) {
|
|
946
|
+
return false;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// find entity type
|
|
950
|
+
for (let i = 0; i < this.props.cache.entities.length; i += 1) {
|
|
951
|
+
if (this.props.cache.entities[i].entityType === entityType) {
|
|
952
|
+
// check if contains task
|
|
953
|
+
for (let c = 0; c < this.props.cache.entities[i].cachedTasks.length; c += 1) {
|
|
954
|
+
if (this.props.cache.entity[i].cachedTasks[c].name === task) {
|
|
955
|
+
return true;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
// does not have task under that entityType
|
|
959
|
+
return false;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
// entityType not cached, should never reach here
|
|
963
|
+
log.error(`${origin}: EntityType not found in cache, should never reach here.`);
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
module.exports = CacheHandler;
|