@itentialopensource/adapter-utils 5.1.5 → 5.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,22 @@
1
1
 
2
+ ## 5.1.7 [09-09-2023]
3
+
4
+ * Added pagination to expanded generic handler
5
+
6
+ Closes ADAPT-2849
7
+
8
+ See merge request itentialopensource/adapter-utils!276
9
+
10
+ ---
11
+
12
+ ## 5.1.6 [09-06-2023]
13
+
14
+ * add fixes to connector that are missing
15
+
16
+ See merge request itentialopensource/adapter-utils!275
17
+
18
+ ---
19
+
2
20
  ## 5.1.5 [08-29-2023]
3
21
 
4
22
  * fix awsroleauth and add authdata to generic
@@ -464,7 +464,12 @@ function makeIAPCall(calls, requestHandler, callback) { // todo pass in properti
464
464
  for (let i = 0; i < calls.length; i += 1) {
465
465
  log.debug('Response :', calls[i].responseFields);
466
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) => {
467
+ const metadata = {};
468
+ if (calls[i].pagination) {
469
+ metadata.pagination = calls[i].pagination;
470
+ metadata.pagination.responseDatakey = calls[i].responseDatakey || '';
471
+ }
472
+ requestHandler.expandedGenericAdapterRequest(metadata, calls[i].path, calls[i].method, null, calls[i].query, calls[i].body, calls[i].headers, (callRet, callErr) => {
468
473
  if (callErr) {
469
474
  log.error('Make iap call failed with error');
470
475
  log.error(callErr);
@@ -408,7 +408,7 @@ function returnStub(request, entitySchema, callProperties) {
408
408
  return callResp;
409
409
  }
410
410
 
411
- const mockresponses = entitySchema.mockresponses;
411
+ const { mockresponses } = entitySchema;
412
412
  let specificResp = null;
413
413
 
414
414
  // if there is a request body, see if there is something that matches a specific input
@@ -814,8 +814,8 @@ function makeRequest(request, entitySchema, callProperties, startTrip, attempt,
814
814
 
815
815
  if (attempt < useRedirect && res.statusCode >= 300 && res.statusCode <= 308 && res.headers.location) {
816
816
  // retries = 0 go here, retries = 1 it doesn't
817
- const newProp = Object.assign({}, callProperties);
818
- const newRequest = Object.assign({}, request);
817
+ const newProp = { ...callProperties };
818
+ const newRequest = { ...request };
819
819
  const nextAtt = attempt + 1;
820
820
 
821
821
  // if there is a protocol on the new location use it
@@ -1931,7 +1931,7 @@ async function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
1931
1931
  }
1932
1932
 
1933
1933
  // remove the path vars from the reqBody
1934
- const actReqBody = Object.assign({}, reqBody);
1934
+ const actReqBody = { ...reqBody };
1935
1935
  if (actReqBody && actReqBody.uriPathVars) {
1936
1936
  delete actReqBody.uriPathVars;
1937
1937
  }
@@ -4304,7 +4304,9 @@ class ConnectorRest {
4304
4304
 
4305
4305
  // if there is a healthcheck schema, over ride the properties
4306
4306
  if (healthSchema) {
4307
- options.path = healthSchema.entitypath;
4307
+ if (!healthcheckpath) {
4308
+ options.path = healthSchema.entitypath;
4309
+ }
4308
4310
  options.method = healthSchema.method;
4309
4311
 
4310
4312
  // save it in memory
@@ -4463,8 +4465,10 @@ class ConnectorRest {
4463
4465
  };
4464
4466
 
4465
4467
  // if there is a healthcheck schema, over ride the properties
4466
- if (healthSchema !== null) {
4468
+ if (healthSchema !== null && !healthcheckpath) {
4467
4469
  request.origPath = healthSchema.entitypath;
4470
+ } else if (healthcheckpath) {
4471
+ request.origPath = healthcheckpath;
4468
4472
  }
4469
4473
 
4470
4474
  // call to make the request
@@ -5,6 +5,54 @@
5
5
  /* eslint consistent-return: warn */
6
6
 
7
7
  /* NodeJS internal utilities */
8
+ const jsonQuery = require('json-query');
9
+
10
+ // INTERNAL FUNCTIONS
11
+
12
+ /**
13
+ * @summary Update the offset for paginated call.
14
+ * @function incrementOffset
15
+ * @param {String} offsetType - type of offset (page or limit)
16
+ * @param {Number} data - previous offset
17
+ * @param {Number} limit - call limit
18
+ * @returns {Number} - new offset
19
+ */
20
+ function incrementOffset(offsetType, previousOffset, limit) {
21
+ let newOffset;
22
+ if (offsetType === 'limit') {
23
+ newOffset = previousOffset + limit;
24
+ } else if (offsetType === 'page') {
25
+ newOffset = previousOffset + 1;
26
+ } else {
27
+ throw new Error(`Offset Type : ${offsetType} not supported or insufficient data was provided`);
28
+ }
29
+ return newOffset;
30
+ }
31
+
32
+ /**
33
+ * @summary Function to set the value at a specified property path
34
+ * @function setNestedProperty
35
+ * @param {Object} oldValue - Object to update
36
+ * @param {String} responseDataKey - path to property to update in . separated string
37
+ * @param {Object} newValue - Object with data to use in update
38
+ */
39
+ function setNestedProperty(oldValue, responseDatakey, newValue) {
40
+ const newRes = jsonQuery(responseDatakey, { data: newValue.response }).value;
41
+ const oldRes = jsonQuery(responseDatakey, { data: oldValue.response }).value;
42
+ const path = responseDatakey.split('.');
43
+ let currentObj = oldValue.response;
44
+ for (let i = 0; i < path.length - 1; i += 1) {
45
+ const segment = path[i];
46
+ if (Object.hasOwnProperty.call(currentObj, segment)) {
47
+ currentObj = currentObj[segment];
48
+ } else {
49
+ currentObj[segment] = {};
50
+ currentObj = currentObj[segment];
51
+ }
52
+ }
53
+ // Modifies value at path and updates original object
54
+ currentObj[path[path.length - 1]] = oldRes.concat(newRes);
55
+ }
8
56
 
9
57
  class GenericHandler {
10
58
  /**
@@ -156,7 +204,7 @@ class GenericHandler {
156
204
  }
157
205
  reqObj.addlHeaders = { ...reqObj.addlHeaders, ...signature };
158
206
  this.requestHandlerInst.identifyRequest('.generic', action, reqObj, returnF, (irReturnData, irReturnError) => {
159
- // if we received an error or their is no response on the results
207
+ // if we received an error or there is no response on the results
160
208
  // return an error
161
209
  if (irReturnError) {
162
210
  /* HERE IS WHERE YOU CAN ALTER THE ERROR MESSAGE */
@@ -174,6 +222,24 @@ class GenericHandler {
174
222
  });
175
223
  });
176
224
  }
225
+
226
+ // Does not support AWS adapters due to the auth call above
227
+ if (metadata && metadata.pagination) {
228
+ return this.expandedGenericAdapterRequestPaginated(metadata.pagination, action, reqObj, returnF, meth, (returnData, returnError) => {
229
+ if (returnError) {
230
+ /* HERE IS WHERE YOU CAN ALTER THE ERROR MESSAGE */
231
+ return callback(null, returnError);
232
+ }
233
+ if (!Object.hasOwnProperty.call(returnData, 'response')) {
234
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Invalid Response', ['genericAdapterRequest'], null, null, null);
235
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
236
+ return callback(null, errorObj);
237
+ }
238
+ /* HERE IS WHERE YOU CAN ALTER THE RETURN DATA */
239
+ // return the response
240
+ return callback(returnData, null);
241
+ });
242
+ }
177
243
  return this.requestHandlerInst.identifyRequest('.generic', action, reqObj, returnF, (irReturnData, irReturnError) => {
178
244
  // if we received an error or their is no response on the results
179
245
  // return an error
@@ -198,6 +264,85 @@ class GenericHandler {
198
264
  }
199
265
  }
200
266
 
267
+ /**
268
+ * Makes the requested generic call
269
+ *
270
+ * @function expandedGenericAdapterRequestPaginated
271
+ * @param {Object} metadata - metadata for the call (optional).
272
+ * Can be a stringified Object.
273
+ * @param {String} uriPath - the path of the api call - do not include the host, port, base path or version (optional)
274
+ * @param {String} restMethod - the rest method (GET, POST, PUT, PATCH, DELETE) (optional)
275
+ * @param {Object} pathVars - the parameters to be put within the url path (optional).
276
+ * Can be a stringified Object.
277
+ * @param {Object} queryData - the parameters to be put on the url (optional).
278
+ * Can be a stringified Object.
279
+ * @param {Object} requestBody - the body to add to the request (optional).
280
+ * Can be a stringified Object.
281
+ * @param {Object} addlHeaders - additional headers to be put on the call (optional).
282
+ * Can be a stringified Object.
283
+ * @param {Object} paginationObject - object specifying pagination variables and increment method
284
+ * @param {getCallback} callback - a callback function to return the result (Generics)
285
+ * or the error
286
+ */
287
+ expandedGenericAdapterRequestPaginated(paginationObject, action, reqObj, returnF, meth, callback) {
288
+ const origin = `${this.myid}-requestHandler-expandedGenericAdapterRequestPaginated`;
289
+ log.trace(origin);
290
+ let results;
291
+ // Set up variables for calls
292
+ const { offsetVar } = paginationObject;
293
+ const pagType = paginationObject.requestLocation; // Body or query supported
294
+ let pagLocation;
295
+ if (pagType === 'body') {
296
+ pagLocation = 'payload';
297
+ } else if (pagType === 'query') {
298
+ pagLocation = 'uriQuery';
299
+ } else {
300
+ const err = new Error(`Pagination Type : ${pagType} not supported or insufficient data was provided`);
301
+ return callback(null, err);
302
+ }
303
+ const limit = reqObj[pagLocation][paginationObject.limitVar];
304
+ const recursiveCall = (currentOffset) => {
305
+ try {
306
+ const myReqObj = {
307
+ ...reqObj
308
+ };
309
+ myReqObj[pagLocation][offsetVar] = currentOffset;
310
+ this.requestHandlerInst.identifyRequest('.generic', action, myReqObj, returnF, (result, error) => {
311
+ if (error) {
312
+ return callback(null, error);
313
+ }
314
+
315
+ if (!Object.hasOwnProperty.call(result, 'response')) {
316
+ const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Invalid Response', ['genericAdapterRequestPaginated'], null, null, null);
317
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
318
+ return callback(null, errorObj);
319
+ }
320
+ // Results hasn't been populated yet
321
+ if (!results) {
322
+ results = { ...result };
323
+ } else if (paginationObject.responseDatakey) {
324
+ setNestedProperty(results, paginationObject.responseDatakey, result);
325
+ } else {
326
+ results.response = results.response.concat(result.response);
327
+ }
328
+ if (result.response.length === limit) {
329
+ const newOffset = incrementOffset(paginationObject.incrementBy, currentOffset, limit);
330
+ // Rescurse
331
+ recursiveCall(newOffset);
332
+ } else {
333
+ return callback(results, null);
334
+ }
335
+ });
336
+ } catch (e) {
337
+ // handle any exception
338
+ const errorObj = this.transUtil.checkAndReturn(e, origin, 'Expanded Generic Adapter Request with Pagination Failed');
339
+ return callback(null, errorObj);
340
+ }
341
+ };
342
+
343
+ recursiveCall(reqObj[pagLocation][offsetVar]);
344
+ }
345
+
201
346
  /**
202
347
  * Makes the requested generic call
203
348
  *
@@ -654,6 +654,10 @@ class RequestHandler {
654
654
 
655
655
  // !! using Generic makes it easier on the Adapter Builder (just need to change the path)
656
656
  // !! you can also replace with a specific call if that is easier
657
+ if (callProps.pagination) {
658
+ metadata.pagination = callProps.pagination;
659
+ metadata.pagination.responseDatakey = callProps.responseDatakey || '';
660
+ }
657
661
  return this.expandedGenericAdapterRequest(metadata, uriPath, uriMethod, null, callQuery, callBody, callHeaders, (result, error) => {
658
662
  // if we received an error or their is no response on the results return an error
659
663
  if (error) {
@@ -1191,7 +1191,9 @@ class RestHandler {
1191
1191
  const bodyString = buildPayload(entity, action, entitySchema, payload);
1192
1192
 
1193
1193
  if ((callMeth !== 'GET' || entitySchema.sendGetBody) && bodyString !== '{}') {
1194
- thisAHdata['Content-length'] = Buffer.byteLength(bodyString);
1194
+ if (!thisAHdata['Content-length'] && !thisAHdata['Content-Length']) {
1195
+ thisAHdata['Content-length'] = Buffer.byteLength(bodyString);
1196
+ }
1195
1197
  }
1196
1198
 
1197
1199
  // set up the request to be sent
@@ -1319,7 +1321,9 @@ class RestHandler {
1319
1321
  const bodyString = buildPayload('.system', 'healthcheck', healthSchema, payload);
1320
1322
 
1321
1323
  if ((callMeth !== 'GET' || healthSchema.sendGetBody) && bodyString !== '{}') {
1322
- thisAHdata['Content-length'] = Buffer.byteLength(bodyString);
1324
+ if (!thisAHdata['Content-length'] && !thisAHdata['Content-Length']) {
1325
+ thisAHdata['Content-length'] = Buffer.byteLength(bodyString);
1326
+ }
1323
1327
  }
1324
1328
 
1325
1329
  // set up the request to be sent
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itentialopensource/adapter-utils",
3
- "version": "5.1.5",
3
+ "version": "5.1.7",
4
4
  "description": "Itential Adapter Utility Libraries",
5
5
  "scripts": {
6
6
  "postinstall": "node utils/setup.js",
Binary file
@@ -1046,6 +1046,38 @@
1046
1046
  "description": "The method of the call to getDevicesFiltered",
1047
1047
  "default": "GET"
1048
1048
  },
1049
+ "pagination": {
1050
+ "type": "object",
1051
+ "description": "todo",
1052
+ "properties": {
1053
+ "offsetVar": {
1054
+ "type": "string",
1055
+ "description": "Name of variable that defines how to go to next set of results"
1056
+ },
1057
+ "limitVar": {
1058
+ "type": "string",
1059
+ "description": "Name of variable that defines the max results returned in a request"
1060
+ },
1061
+ "incrementBy": {
1062
+ "type": "string",
1063
+ "enum": [
1064
+ "limit",
1065
+ "page"
1066
+ ],
1067
+ "description": "How to incremenet offset. Default limit",
1068
+ "default": "limit"
1069
+ },
1070
+ "requestLocation": {
1071
+ "type": "string",
1072
+ "enum": [
1073
+ "query",
1074
+ "body"
1075
+ ],
1076
+ "description": "Where in request the pagination data goes",
1077
+ "default": "query"
1078
+ }
1079
+ }
1080
+ },
1049
1081
  "query": {
1050
1082
  "type": "object",
1051
1083
  "description": "The json object with query parameters of the call to getDevicesFiltered",
@@ -1402,31 +1434,63 @@
1402
1434
  "properties": {
1403
1435
  "path": {
1404
1436
  "type": "string",
1405
- "description": "The fully qualified path of the call to getDevice (e.g. /rest/api/device/{deviceid})",
1437
+ "description": "The fully qualified path of the call to populate the cache (e.g. /rest/api/devices)",
1406
1438
  "default": ""
1407
1439
  },
1408
1440
  "method": {
1409
1441
  "type": "string",
1410
- "description": "The method of the call to getDevice",
1442
+ "description": "The method of the call to populate the cache",
1411
1443
  "default": "GET"
1412
1444
  },
1445
+ "pagination": {
1446
+ "type": "object",
1447
+ "description": "todo",
1448
+ "properties": {
1449
+ "offsetVar": {
1450
+ "type": "string",
1451
+ "description": "Name of variable that defines how to go to next set of results"
1452
+ },
1453
+ "limitVar": {
1454
+ "type": "string",
1455
+ "description": "Name of variable that defines the max results returned in a request"
1456
+ },
1457
+ "incrementBy": {
1458
+ "type": "string",
1459
+ "enum": [
1460
+ "limit",
1461
+ "page"
1462
+ ],
1463
+ "description": "How to incremenet offset. Default limit",
1464
+ "default": "limit"
1465
+ },
1466
+ "requestLocation": {
1467
+ "type": "string",
1468
+ "enum": [
1469
+ "query",
1470
+ "body"
1471
+ ],
1472
+ "description": "Where in request the pagination data goes",
1473
+ "default": "query"
1474
+ }
1475
+ }
1476
+ },
1413
1477
  "query": {
1414
1478
  "type": "object",
1415
- "description": "The json object with query parameters of the call to getDevice",
1479
+ "description": "The json object with query parameters of the call to populate the cache",
1416
1480
  "additionalProperties": {
1417
1481
  "type": ["string", "number"]
1418
1482
  }
1419
1483
  },
1420
1484
  "body": {
1421
1485
  "type": "object",
1422
- "description": "The json object with body of the call to getDevice",
1486
+ "description": "The json object with body of the call to populate the cache",
1423
1487
  "additionalProperties": {
1424
1488
  "type": ["string", "number"]
1425
1489
  }
1426
1490
  },
1427
1491
  "headers": {
1428
1492
  "type": "object",
1429
- "description": "The json object with headers of the call to getDevice",
1493
+ "description": "The json object with headers of the call to populate the cache",
1430
1494
  "additionalProperties": {
1431
1495
  "type": ["string", "number"]
1432
1496
  }
@@ -1442,7 +1506,7 @@
1442
1506
  },
1443
1507
  "requestFields": {
1444
1508
  "type": "object",
1445
- "description": "The json object with response fields of the call to getDevice",
1509
+ "description": "The json object with response fields of the call to populate the cache",
1446
1510
  "additionalProperties": {
1447
1511
  "type": ["string", "number"]
1448
1512
  },
@@ -1455,7 +1519,7 @@
1455
1519
  },
1456
1520
  "responseFields": {
1457
1521
  "type": "object",
1458
- "description": "The json object with response fields of the call to getDevice",
1522
+ "description": "The json object with response fields of the call to populate the cache",
1459
1523
  "additionalProperties": {
1460
1524
  "type": ["string", "number"]
1461
1525
  }