@itentialopensource/adapter-utils 5.5.1 → 5.7.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 CHANGED
@@ -1,4 +1,22 @@
1
1
 
2
+ ## 5.7.0 [08-21-2024]
3
+
4
+ * Update gzip logic
5
+
6
+ See merge request itentialopensource/adapter-utils!297
7
+
8
+ ---
9
+
10
+ ## 5.6.0 [07-31-2024]
11
+
12
+ * Add abilities to refresh token and set msa query
13
+
14
+ Closes ADAPT-3629
15
+
16
+ See merge request itentialopensource/adapter-utils!294
17
+
18
+ ---
19
+
2
20
  ## 5.5.1 [07-30-2024]
3
21
 
4
22
  * Fix gzip conditional in propertyUtils
@@ -108,6 +108,9 @@ const healthchecklock = 0;
108
108
  let encodeUri = true;
109
109
  let authQueryEncode = null;
110
110
  let addCreds = true;
111
+ let refTokenReq = null;
112
+ let refTokenTimeout = -1;
113
+ let runRefreshToken = false;
111
114
 
112
115
  // Other global variables
113
116
  let id = null;
@@ -777,34 +780,61 @@ function makeRequest(request, entitySchema, callProperties, startTrip, attempt,
777
780
  // make the call to System
778
781
  const httpRequest = useProt.request(request.header, (res) => {
779
782
  let respStr = '';
780
- res.setEncoding('utf8');
781
-
783
+ if (res.headers['content-encoding'] !== 'gzip') {
784
+ res.setEncoding('utf8');
785
+ }
786
+ const chunks = [];
782
787
  // process data from response
783
788
  res.on('data', (replyData) => {
789
+ chunks.push(replyData);
784
790
  respStr += replyData;
785
791
  });
786
792
 
787
793
  // process end of response
788
794
  res.on('end', () => {
795
+ let buffer = '';
796
+ if (res.headers['content-encoding'] === 'gzip') {
797
+ buffer = Buffer.concat(chunks);
798
+ }
799
+
789
800
  const tripDiff = process.hrtime(roundTTime);
790
801
  const tripEnd = `${Math.round(((tripDiff[0] * NS_PER_SEC) + tripDiff[1]) / 1000000)}ms`;
791
- const callResp = {
792
- status: 'success',
793
- code: res.statusCode,
794
- headers: res.headers,
795
- response: respStr,
796
- request: {
797
- protocol: protStr,
798
- host: request.header.hostname,
799
- port: request.header.port,
800
- uri: request.header.path,
801
- method: request.header.method,
802
- payload: request.body
803
- },
804
- redirects: attempt,
805
- tripTime: tripEnd
806
- };
807
-
802
+ let callResp = {};
803
+ if (res.headers['content-encoding'] === 'gzip') {
804
+ callResp = {
805
+ status: 'success',
806
+ code: res.statusCode,
807
+ headers: res.headers,
808
+ response: buffer,
809
+ request: {
810
+ protocol: protStr,
811
+ host: request.header.hostname,
812
+ port: request.header.port,
813
+ uri: request.header.path,
814
+ method: request.header.method,
815
+ payload: request.body
816
+ },
817
+ redirects: attempt,
818
+ tripTime: tripEnd
819
+ };
820
+ } else {
821
+ callResp = {
822
+ status: 'success',
823
+ code: res.statusCode,
824
+ headers: res.headers,
825
+ response: respStr,
826
+ request: {
827
+ protocol: protStr,
828
+ host: request.header.hostname,
829
+ port: request.header.port,
830
+ uri: request.header.path,
831
+ method: request.header.method,
832
+ payload: request.body
833
+ },
834
+ redirects: attempt,
835
+ tripTime: tripEnd
836
+ };
837
+ }
808
838
  // if there was a cookie or biscotti returned, need to set cookie on the new request
809
839
  if (request.header.headers) {
810
840
  const headKeys = Object.keys(request.header.headers);
@@ -1269,6 +1299,7 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
1269
1299
  const currResult = {
1270
1300
  token: null,
1271
1301
  tokenp2: null,
1302
+ refreshToken: null,
1272
1303
  front: tokenSchema.responseSchema.properties.token.front,
1273
1304
  end: tokenSchema.responseSchema.properties.token.end
1274
1305
  };
@@ -1558,6 +1589,10 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
1558
1589
  && tokenSchema.responseSchema.properties.expires.placement && tokenSchema.responseSchema.properties.expires.placement.toUpperCase() === 'BODY') {
1559
1590
  currResult.expires = translated.expires;
1560
1591
  }
1592
+ if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.refreshToken
1593
+ && tokenSchema.responseSchema.properties.refreshToken.placement && tokenSchema.responseSchema.properties.refreshToken.placement.toUpperCase() === 'BODY') {
1594
+ currResult.refreshToken = translated.refreshToken;
1595
+ }
1561
1596
  // return the token that we find in the translated object
1562
1597
  return resolve(currResult);
1563
1598
  });
@@ -1587,6 +1622,9 @@ async function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
1587
1622
  if (callProperties && callProperties.mfa) {
1588
1623
  entity = callProperties.mfa.stepAtionName;
1589
1624
  }
1625
+ if (runRefreshToken) {
1626
+ entity = 'getRefreshToken';
1627
+ }
1590
1628
  // Get the entity schema from the file system
1591
1629
  return propUtilInst.getEntitySchema('.system', entity, choosepath, this.dbUtil, async (tokenSchema, healthError) => {
1592
1630
  if (healthError || !tokenSchema || Object.keys(tokenSchema).length === 0) {
@@ -2062,19 +2100,37 @@ async function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
2062
2100
  options.path = options.path.replace('{password}', usePass);
2063
2101
  }
2064
2102
 
2065
- // if this is not a get, need to add the info to the request
2066
2103
  let creds = {};
2067
- if (authMethod === 'multi_step_authentication') {
2068
- const { stepBody, stepHeaders } = callProperties.mfa;
2104
+ if (authMethod === 'multi_step_authentication' && !runRefreshToken) {
2105
+ const { stepBody, stepHeaders, stepQuery } = callProperties.mfa;
2069
2106
  Object.assign(creds, stepBody);
2070
2107
  Object.assign(options.headers, stepHeaders);
2108
+ // If there are query variables, add them to the path
2109
+ if (Object.keys(stepQuery).length > 0) {
2110
+ let mfaQueryString = '';
2111
+ if (encodeUri === true) {
2112
+ mfaQueryString += querystring.stringify(stepQuery);
2113
+ } else {
2114
+ const mfaQqueryKeys = Object.keys(stepQuery);
2115
+ for (let k = 0; k < mfaQqueryKeys.length; k += 1) {
2116
+ if (k > 0) {
2117
+ mfaQueryString += '&';
2118
+ }
2119
+ mfaQueryString += `${mfaQqueryKeys[k]}=${stepQuery[mfaQqueryKeys[k]]}`;
2120
+ }
2121
+ }
2122
+ // append to the path
2123
+ if (options.path.indexOf('?') < 0) {
2124
+ options.path = `${options.path}?${mfaQueryString}`;
2125
+ } else {
2126
+ options.path = `${options.path}&${mfaQueryString}`;
2127
+ }
2128
+ }
2071
2129
  }
2130
+
2131
+ // if this is not a get, need to add the info to the request
2072
2132
  if (options.method !== 'GET') {
2073
- if (authMethod === 'multi_step_authentication') {
2074
- const { stepBody, stepHeaders } = callProperties.mfa;
2075
- Object.assign(creds, stepBody);
2076
- Object.assign(options.headers, stepHeaders);
2077
- } else {
2133
+ if (authMethod !== 'multi_step_authentication') {
2078
2134
  creds = {
2079
2135
  username: useUser,
2080
2136
  password: usePass
@@ -2139,6 +2195,34 @@ async function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
2139
2195
  }
2140
2196
  }
2141
2197
 
2198
+ // Update request info if run refresh token
2199
+ if (runRefreshToken && callProperties.refRequest && Object.keys(callProperties.refRequest).length > 0) {
2200
+ const { headers, query, body } = callProperties.refRequest;
2201
+ Object.assign(options.headers, headers);
2202
+ Object.assign(creds, body);
2203
+ // If there are query variables, add them to the path
2204
+ if (Object.keys(query).length > 0) {
2205
+ let refTokenQueryString = '';
2206
+ if (encodeUri === true) {
2207
+ refTokenQueryString += querystring.stringify(query);
2208
+ } else {
2209
+ const refTokenQueryKeys = Object.keys(query);
2210
+ for (let k = 0; k < refTokenQueryKeys.length; k += 1) {
2211
+ if (k > 0) {
2212
+ refTokenQueryString += '&';
2213
+ }
2214
+ refTokenQueryString += `${refTokenQueryKeys[k]}=${query[refTokenQueryKeys[k]]}`;
2215
+ }
2216
+ }
2217
+ // append to the path
2218
+ if (options.path.indexOf('?') < 0) {
2219
+ options.path = `${options.path}?${refTokenQueryString}`;
2220
+ } else {
2221
+ options.path = `${options.path}&${refTokenQueryString}`;
2222
+ }
2223
+ }
2224
+ }
2225
+
2142
2226
  // map the data we received to an Entity - will get back the defaults
2143
2227
  const tokenEntity = transUtilInst.mapToOutboundEntity(creds, tokenSchema.requestSchema);
2144
2228
  bodyString = tokenEntity;
@@ -2267,7 +2351,12 @@ function addTokenItem(user, reqBody, token, timeout, callback) {
2267
2351
 
2268
2352
  // add the token item to the tokenlist and return it
2269
2353
  if (tokenCache === 'local') {
2270
- tokenList.push(tokenItem);
2354
+ const index = tokenList.findIndex((obj) => obj.tkey === tokenItem.tkey);
2355
+ if (index === -1) {
2356
+ tokenList.push(tokenItem);
2357
+ } else {
2358
+ tokenList[index] = tokenItem;
2359
+ }
2271
2360
  return callback(token);
2272
2361
  }
2273
2362
 
@@ -2344,13 +2433,21 @@ function validToken(user, reqBody, invalidToken, callback) {
2344
2433
  if (tokenList[i].tkey === tkey) {
2345
2434
  // if the token expired (or will expire within a minute),
2346
2435
  // or it is invalid (failed) remove it from the token list
2347
- if (tokenList[i].expire < expireCheck
2348
- || tokenList[i].token.token === invalidToken) {
2436
+ if ((tokenList[i].expire < expireCheck
2437
+ || tokenList[i].token.token === invalidToken) && !tokenList[i].token.refreshToken) {
2349
2438
  tokenList.splice(i, 1);
2350
2439
  break;
2440
+ } else if ((tokenList[i].expire < expireCheck
2441
+ || tokenList[i].token.token === invalidToken) && tokenList[i].token.refreshToken) {
2442
+ // Just keep refreshToken for future token refresh
2443
+ delete tokenList[i].expire;
2444
+ delete tokenList[i].token.token;
2445
+ delete tokenList[i].token.tokenp2;
2446
+ break;
2447
+ }
2448
+ if (tokenList[i].token.token || tokenList[i].token.tokenp2) {
2449
+ retToken = tokenList[i].token;
2351
2450
  }
2352
-
2353
- retToken = tokenList[i].token;
2354
2451
  break;
2355
2452
  }
2356
2453
  }
@@ -2362,6 +2459,71 @@ function validToken(user, reqBody, invalidToken, callback) {
2362
2459
  }
2363
2460
  }
2364
2461
 
2462
+ /*
2463
+ * INTERNAL FUNCTION: retrieve the refresh token from cache and determine if
2464
+ * it valid. if valid, return it otherwise return null
2465
+ */
2466
+ function validRefreshToken(user, reqBody, invalidToken, callback) {
2467
+ const origin = `${id}-connectorRest-validRefreshToken`;
2468
+ log.trace(origin);
2469
+
2470
+ try {
2471
+ // get the token from redis
2472
+ let tkey = `${id}__%%__${user}`;
2473
+
2474
+ if (reqBody) {
2475
+ tkey += `__%%__${JSON.stringify(reqBody)}`;
2476
+ }
2477
+
2478
+ if (refTokenReq && refTokenReq.refresh_token && refTokenReq.refresh_token.token_timeout) {
2479
+ refTokenTimeout = refTokenReq.refresh_token.token_timeout;
2480
+ }
2481
+
2482
+ // Return null if refresh token timeout is -1
2483
+ if (refTokenTimeout === -1) {
2484
+ return callback(null);
2485
+ }
2486
+
2487
+ const currentTime = new Date().getTime();
2488
+
2489
+ // To check if refresh token will expire within a minute
2490
+ const refTokenTimeoutMillis = refTokenTimeout - 60000;
2491
+
2492
+ let refToken = null;
2493
+
2494
+ // find user's refresh token in the list
2495
+ for (let i = 0; i < tokenList.length; i += 1) {
2496
+ if (tokenList[i].tkey === tkey) {
2497
+ if (currentTime - tokenList[i].start < refTokenTimeoutMillis && tokenList[i].token.refreshToken) {
2498
+ refToken = tokenList[i].token.refreshToken;
2499
+ break;
2500
+ }
2501
+ break;
2502
+ }
2503
+ }
2504
+ return callback(refToken);
2505
+ } catch (e) {
2506
+ // handle any exception
2507
+ const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue validating cached refresh token');
2508
+ return callback(null, errorObj);
2509
+ }
2510
+ }
2511
+
2512
+ function getCachedRefToken(cachedTokenIdentifier, invalidToken) {
2513
+ const origin = `${id}-connectorRest-getCachedRefToken`;
2514
+ log.trace(origin);
2515
+
2516
+ return new Promise((resolve, reject) => {
2517
+ validRefreshToken(id, cachedTokenIdentifier, invalidToken, (refToken, error) => {
2518
+ if (error) {
2519
+ reject(error);
2520
+ } else {
2521
+ resolve(refToken);
2522
+ }
2523
+ });
2524
+ });
2525
+ }
2526
+
2365
2527
  /*
2366
2528
  * INTERNAL FUNCTION: getTokenItem is used to retrieve a token items from
2367
2529
  * memory based on the user provided
@@ -2382,6 +2544,27 @@ function getTokenItem(pathForToken, user, reqBody, invalidToken, callProperties,
2382
2544
  if (retToken !== null) {
2383
2545
  done(retToken, null);
2384
2546
  } else {
2547
+ // If primary token not found or invalid, check if there is refresh token
2548
+ validRefreshToken(user, reqBody, invalidToken, (refToken, rerror) => {
2549
+ if (rerror) {
2550
+ done(null, rerror);
2551
+ }
2552
+ if (refToken !== null) {
2553
+ log.debug(`${origin}-returning cached refresh token`);
2554
+ if (!callProperties) {
2555
+ callProperties = {};
2556
+ }
2557
+ callProperties.refRequest = {};
2558
+ buildRefTokenData(refToken, callProperties);
2559
+ // Check if getRefreshToken entity exists
2560
+ propUtilInst.getEntitySchema('.system', 'getRefreshToken', choosepath, this.dbUtil, async (tokenSchema, error) => {
2561
+ // Refresh the token if getRefreshToken entity exists
2562
+ if (tokenSchema) {
2563
+ runRefreshToken = true;
2564
+ }
2565
+ });
2566
+ } else runRefreshToken = false;
2567
+ });
2385
2568
  // No valid token found, Need to get a new token and add it to the token list
2386
2569
  buildTokenRequest(pathForToken, reqBody, callProperties, (dyntoken, berror) => {
2387
2570
  if (berror) {
@@ -2405,7 +2588,8 @@ function getTokenItem(pathForToken, user, reqBody, invalidToken, callProperties,
2405
2588
 
2406
2589
  const tokenObj = {
2407
2590
  token: findPrimaryTokenInResult(dyntoken, dyntoken.front, dyntoken.end),
2408
- tokenp2: findSecondaryTokenInResult(dyntoken, dyntoken.front, dyntoken.end)
2591
+ tokenp2: findSecondaryTokenInResult(dyntoken, dyntoken.front, dyntoken.end),
2592
+ refreshToken: dyntoken.refreshToken
2409
2593
  };
2410
2594
 
2411
2595
  // if this is worth caching
@@ -2679,6 +2863,24 @@ function buildMfaSso(prop, mfaStepConfiguration, callProperties) {
2679
2863
  }
2680
2864
  }
2681
2865
 
2866
+ function buildMfaQuery(prop, mfaStepConfiguration, callProperties) {
2867
+ const origin = `${id}-connectorRest-buildMfaQuery`;
2868
+ log.trace(origin);
2869
+ const fullPropertyValue = mfaStepConfiguration.requestFields[prop];
2870
+ // Check if fullPropertyValue has reference in curly braces (e.g., {getSession.responseFields.session})
2871
+ let propertyValue = fullPropertyValue;
2872
+ const matching = searchTextInCurlyBraces(fullPropertyValue);
2873
+ const queryParam = prop.split('.')[1];
2874
+ if (matching) {
2875
+ const matchedText = matching[0];
2876
+ propertyValue = matching[1];
2877
+ const resolvedValue = getReferencedMfaValue(propertyValue);
2878
+ callProperties.mfa.stepQuery[queryParam] = fullPropertyValue.replace(matchedText, resolvedValue);
2879
+ } else {
2880
+ callProperties.mfa.stepQuery[queryParam] = mfaStepConfiguration.requestFields[prop];
2881
+ }
2882
+ }
2883
+
2682
2884
  /* */
2683
2885
  function getReferencedMfaValue(propertyValue) {
2684
2886
  const origin = `${id}-connectorRest-getReferencedMfaValue`;
@@ -2702,6 +2904,7 @@ function buildMfaStepData(step, callProperties) {
2702
2904
  const mfaStepConfiguration = multiStepAuthCalls[step];
2703
2905
  callProperties.mfa.stepBody = {};
2704
2906
  callProperties.mfa.stepHeaders = {};
2907
+ callProperties.mfa.stepQuery = {};
2705
2908
  if (mfaStepConfiguration.sso) {
2706
2909
  callProperties.mfa.host = '';
2707
2910
  callProperties.mfa.port = '';
@@ -2715,6 +2918,8 @@ function buildMfaStepData(step, callProperties) {
2715
2918
  Object.keys(mfaStepConfiguration.requestFields).forEach((prop) => {
2716
2919
  if (prop.startsWith('header')) {
2717
2920
  buildMfaHeader(prop, mfaStepConfiguration, callProperties);
2921
+ } else if (prop.startsWith('query')) {
2922
+ buildMfaQuery(prop, mfaStepConfiguration, callProperties);
2718
2923
  } else {
2719
2924
  buildMfaBody(prop, mfaStepConfiguration, callProperties);
2720
2925
  }
@@ -2722,6 +2927,35 @@ function buildMfaStepData(step, callProperties) {
2722
2927
  }
2723
2928
  }
2724
2929
 
2930
+ function buildRefTokenData(cachedRefToken, callProperties) {
2931
+ const origin = `${id}-connectorRest-buildRefTokenData`;
2932
+ log.trace(origin);
2933
+ if (refTokenReq && refTokenReq.requestFields && Object.keys(refTokenReq.requestFields).length > 0) {
2934
+ callProperties.refRequest.headers = {};
2935
+ callProperties.refRequest.query = {};
2936
+ callProperties.refRequest.body = {};
2937
+ Object.keys(refTokenReq.requestFields).forEach((prop) => {
2938
+ if (prop.startsWith('header')) {
2939
+ const headerName = prop.split('.')[1];
2940
+ callProperties.refRequest.headers[headerName] = refTokenReq.requestFields[prop];
2941
+ } else if (prop.startsWith('query')) {
2942
+ const queryName = prop.split('.')[1];
2943
+ callProperties.refRequest.query[queryName] = refTokenReq.requestFields[prop];
2944
+ } else {
2945
+ callProperties.refRequest.body[prop] = refTokenReq.requestFields[prop];
2946
+ }
2947
+ });
2948
+ }
2949
+
2950
+ if (refTokenReq && refTokenReq.refresh_token && Object.keys(refTokenReq.refresh_token).length > 0) {
2951
+ if (refTokenReq.refresh_token.placement && refTokenReq.refresh_token.placement.toLowerCase() === 'query') {
2952
+ callProperties.refRequest.query.refreshToken = cachedRefToken;
2953
+ } else {
2954
+ callProperties.refRequest.body.refreshToken = cachedRefToken;
2955
+ }
2956
+ }
2957
+ }
2958
+
2725
2959
  /* */
2726
2960
  async function translateMfaStepResult(step, stepResult, callProperties) {
2727
2961
  const origin = `${id}-connectorRest-translateMfaStepResult`;
@@ -2738,7 +2972,7 @@ async function translateMfaStepResult(step, stepResult, callProperties) {
2738
2972
  allProperties.forEach((prop) => {
2739
2973
  if (Object.prototype.hasOwnProperty.call(stepResult, prop)) {
2740
2974
  const externalName = tokenSchema.responseSchema.properties[prop].external_name;
2741
- if (!stepResult[prop]) {
2975
+ if (!stepResult[prop] && prop !== 'refreshToken') {
2742
2976
  log.error(`No step-${step + 1} result for responseSchema (prop=>external_name): (${prop}=>${externalName}) found in step response`);
2743
2977
  return reject(new Error(`Response schema for step-${step + 1} misconfiguration`));
2744
2978
  }
@@ -2820,8 +3054,43 @@ async function getMfaFinalTokens(request, callProperties, invalidToken) {
2820
3054
  log.debug(`${origin}-returning cached MFA token`);
2821
3055
  finalTokens = cachedToken;
2822
3056
  return;
2823
- } mfaStepsResults.length = 0;
2824
- // no cached token found, trigger auth steps to obtain new token
3057
+ }
3058
+ // Get cached refresh token if primary token is not valid
3059
+ const cachedRefToken = await getCachedRefToken(cachedTokenIdentifier, invalidToken)
3060
+ .catch((error) => {
3061
+ log.error(error);
3062
+ throw error;
3063
+ });
3064
+ if (cachedRefToken) {
3065
+ log.debug(`${origin}-returning cached MFA refresh token`);
3066
+ if (!callProperties) {
3067
+ callProperties = {};
3068
+ }
3069
+ callProperties.refRequest = {};
3070
+ buildRefTokenData(cachedRefToken, callProperties);
3071
+ propUtilInst.getEntitySchema('.system', 'getRefreshToken', choosepath, this.dbUtil, async (tokenSchema, error) => {
3072
+ if (error || !tokenSchema || Object.keys(tokenSchema).length === 0) {
3073
+ tokenSchema = null;
3074
+ }
3075
+ // If cached refresh token is valid and there is action for getRefreshToken, run refresh token
3076
+ if (tokenSchema) {
3077
+ runRefreshToken = true;
3078
+ }
3079
+ });
3080
+ if (runRefreshToken) {
3081
+ finalTokens = await buildTokenRequest(request.header.path, request.authData, callProperties).catch((error) => { // eslint-disable-line no-await-in-loop
3082
+ log.error(error);
3083
+ throw error;
3084
+ });
3085
+ if (finalTokens) {
3086
+ await cacheMfaToken(cachedTokenIdentifier, finalTokens).catch((error) => log.error(error));
3087
+ }
3088
+ return;
3089
+ }
3090
+ }
3091
+ runRefreshToken = false;
3092
+ mfaStepsResults.length = 0;
3093
+ // no cached primary token or refresh token found, trigger auth steps to obtain new token
2825
3094
  for (let step = 0; step < multiStepAuthCalls.length; step += 1) {
2826
3095
  const stepAtionName = `MFA_Step_${step + 1}`;
2827
3096
  log.debug(`${origin}-Executing MFA step-${step + 1}, action: ${stepAtionName}`);
@@ -4028,6 +4297,11 @@ class ConnectorRest {
4028
4297
  if (typeof props.authentication.addCreds === 'boolean') {
4029
4298
  addCreds = props.authentication.addCreds;
4030
4299
  }
4300
+
4301
+ // set the refresh token request (optional - default is null)
4302
+ if (props.authentication.refresh_token_request && typeof props.authentication.refresh_token_request === 'object') {
4303
+ refTokenReq = props.authentication.refresh_token_request;
4304
+ }
4031
4305
  }
4032
4306
 
4033
4307
  // set the stub mode (optional - default is false)
@@ -534,8 +534,8 @@ class AdapterPropertyUtil {
534
534
  }
535
535
  if (entitySchema.responseDatatype.toUpperCase() !== 'JSON' && entitySchema.responseDatatype.toUpperCase() !== 'XML'
536
536
  && entitySchema.responseDatatype.toUpperCase() !== 'URLENCODE' && entitySchema.responseDatatype.toUpperCase() !== 'XML2JSON'
537
- && entitySchema.requestDatatype.toUpperCase() !== 'GZIP2XML2JSON' && entitySchema.requestDatatype.toUpperCase() !== 'GZIP2XML'
538
- && entitySchema.requestDatatype.toUpperCase() !== 'GZIP2JSON') {
537
+ && entitySchema.responseDatatype.toUpperCase() !== 'GZIP2XML2JSON' && entitySchema.responseDatatype.toUpperCase() !== 'GZIP2XML'
538
+ && entitySchema.responseDatatype.toUpperCase() !== 'GZIP2JSON' && entitySchema.responseDatatype.toUpperCase() !== 'GZIP2PLAIN') {
539
539
  entitySchema.responseDatatype = 'PLAIN';
540
540
  }
541
541
 
@@ -952,7 +952,9 @@ class AdapterPropertyUtil {
952
952
  entitySchema.requestDatatype = 'PLAIN';
953
953
  }
954
954
  if (entitySchema.responseDatatype.toUpperCase() !== 'JSON' && entitySchema.responseDatatype.toUpperCase() !== 'XML'
955
- && entitySchema.responseDatatype.toUpperCase() !== 'URLENCODE' && entitySchema.responseDatatype.toUpperCase() !== 'XML2JSON') {
955
+ && entitySchema.responseDatatype.toUpperCase() !== 'URLENCODE' && entitySchema.responseDatatype.toUpperCase() !== 'XML2JSON'
956
+ && entitySchema.responseDatatype.toUpperCase() !== 'GZIP2XML2JSON' && entitySchema.responseDatatype.toUpperCase() !== 'GZIP2XML'
957
+ && entitySchema.responseDatatype.toUpperCase() !== 'GZIP2JSON' && entitySchema.responseDatatype.toUpperCase() !== 'GZIP2PLAIN') {
956
958
  entitySchema.responseDatatype = 'PLAIN';
957
959
  }
958
960
 
@@ -8,7 +8,7 @@ const querystring = require('querystring');
8
8
  const jsonQuery = require('json-query');
9
9
  const jsonxml = require('jsontoxml');
10
10
  const xml2js = require('xml2js');
11
- const pako = require('pako');
11
+ const zlib = require('zlib');
12
12
 
13
13
  const globalSchema = JSON.parse(require('fs').readFileSync(require('path').join(__dirname, '/../schemas/globalSchema.json')));
14
14
 
@@ -53,19 +53,6 @@ function matchResponse(uriPath, method, type, mockresponses) {
53
53
  return null;
54
54
  }
55
55
 
56
- /*
57
- * INTERNAL FUNCTION: Parse XML
58
- */
59
- function parseXML(xmlString, callback) {
60
- const parser = new xml2js.Parser({ explicitArray: false, attrkey: '_attr' });
61
- parser.parseString(xmlString, (error, result) => {
62
- if (error) {
63
- return callback(error);
64
- }
65
- return callback(null, result);
66
- });
67
- }
68
-
69
56
  /*
70
57
  * INTERNAL FUNCTION: recursively inspect body data if heirarchical
71
58
  */
@@ -491,62 +478,58 @@ function handleRestRequest(request, entityId, entitySchema, callProperties, filt
491
478
  if (entitySchema && entitySchema.responseDatatype && entitySchema.responseDatatype.toUpperCase() === 'URLENCODE') {
492
479
  // return the response
493
480
  retResponse = querystring.parse(resObj.response.trim());
494
- } else if (entitySchema && entitySchema.responseDatatype && (entitySchema.responseDatatype.toUpperCase() === 'GZIP2PLAIN'
495
- || entitySchema.responseDatatype.toUpperCase() === 'GZIP2JSON' || entitySchema.responseDatatype.toUpperCase() === 'GZIP2XML'
496
- || entitySchema.responseDatatype.toUpperCase() === 'GZIP2XML2JSON')) {
497
- // If response is GZIP
481
+ } if (entitySchema && entitySchema.responseDatatype
482
+ && ['GZIP2PLAIN', 'GZIP2JSON', 'GZIP2XML', 'GZIP2XML2JSON'].includes(entitySchema.responseDatatype.toUpperCase())) {
498
483
  const responseDatatype = entitySchema.responseDatatype.toUpperCase();
499
- const responseBody = pako.inflate(Buffer.from(resObj.response, 'binary'), { to: 'string' });
500
- // Handle conversion based on responseDatatype
501
- if (responseDatatype === 'GZIP2PLAIN') {
502
- retObject.response = responseBody;
503
- } else if (responseDatatype === 'GZIP2JSON') {
504
- // Parse responseBody as JSON
505
- retObject.response = JSON.parse(responseBody);
506
- } else if (responseDatatype === 'GZIP2XML' || responseDatatype === 'GZIP2XML2JSON') {
507
- parseXML(responseBody, (error, xmlResult) => {
508
- if (error) {
509
- log.warn(`${origin}: Error parsing XML response: ${error}`);
484
+
485
+ return zlib.gunzip(resObj.response, (err, unzipresponse) => {
486
+ if (err) {
487
+ log.warn(`${origin}: Error inflating gzip response: ${err}`);
488
+ return callback(retObject);
489
+ }
490
+
491
+ const responseBody = unzipresponse.toString('utf8'); // Convert buffer to string
492
+ if (responseDatatype === 'GZIP2PLAIN') {
493
+ retObject.response = responseBody;
494
+ log.debug(`${origin}: RESPONSE: ${retObject.response}`);
495
+ return callback(retObject);
496
+ } if (responseDatatype === 'GZIP2JSON') {
497
+ try {
498
+ retObject.response = JSON.parse(responseBody);
499
+ log.debug(`${origin}: RESPONSE: ${retObject.response}`);
500
+ return callback(retObject); // Parse responseBody as JSON
501
+ } catch (jsonErr) {
502
+ log.warn(`${origin}: Error parsing JSON response: ${jsonErr}`);
510
503
  return callback(retObject);
511
504
  }
512
-
513
- retObject.response = xmlResult;
514
-
515
- if (responseDatatype === 'GZIP2XML2JSON') {
516
- // Optionally convert XML to JSON
517
- try {
518
- const parser = new xml2js.Parser({ explicitArray: false, attrkey: '_attr' });
519
- return parser.parseString(xmlResult, (err, result) => {
520
- if (err) {
521
- log.warn(`${origin}: Unable to parse xml to json ${err}`);
522
- return callback(retObject);
523
- }
524
- // Logic to convert keys specified to array if object
525
- if (xmlArrayKeys) {
526
- setArrays(result, xmlArrayKeys);
527
- }
528
- retObject.response = result;
529
- return callback(retObject);
530
- });
531
- } catch (ex) {
532
- log.warn(`${origin}: Unable to get json from xml ${ex}`);
505
+ } else if (responseDatatype === 'GZIP2XML' || responseDatatype === 'GZIP2XML2JSON') {
506
+ xml2js.parseString(responseBody, { explicitArray: false, attrkey: '_attr' }, (xmlErr, xmlResult) => {
507
+ if (xmlErr) {
508
+ log.warn(`${origin}: Error parsing XML response: ${xmlErr}`);
533
509
  return callback(retObject);
534
510
  }
535
- }
536
511
 
537
- log.debug(`${origin}: RESPONSE: ${retObject.response}`);
538
- return callback(retObject);
539
- });
540
- }
541
- } else {
542
- // process the response - parse it
543
- try {
544
- retResponse = JSON.parse(resObj.response.trim());
545
- } catch (ex) {
546
- // otherwise log parse failure and return the unparsed response
547
- log.warn(`${origin}: An error occurred parsing the resulting JSON: ${ex}: Actual Response is: ${resObj}`);
512
+ retObject.response = xmlResult;
513
+
514
+ if (responseDatatype === 'GZIP2XML2JSON') {
515
+ // Optionally convert XML to JSON, already done above by xml2js
516
+ if (xmlArrayKeys) {
517
+ setArrays(retObject.response, xmlArrayKeys); // Handle array conversion
518
+ }
519
+ }
520
+ return callback(retObject);
521
+ });
522
+ }
548
523
  return callback(retObject);
549
- }
524
+ });
525
+ }
526
+ // process the response - parse it
527
+ try {
528
+ retResponse = JSON.parse(resObj.response.trim());
529
+ } catch (ex) {
530
+ // otherwise log parse failure and return the unparsed response
531
+ log.warn(`${origin}: An error occurred parsing the resulting JSON: ${ex}: Actual Response is: ${resObj}`);
532
+ return callback(retObject);
550
533
  }
551
534
 
552
535
  // Make the call to translate the received Entity to Pronghorn Entity
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itentialopensource/adapter-utils",
3
- "version": "5.5.1",
3
+ "version": "5.7.0",
4
4
  "description": "Itential Adapter Utility Libraries",
5
5
  "scripts": {
6
6
  "postinstall": "node utils/setup.js",
@@ -27,7 +27,7 @@
27
27
  "dependencies": {
28
28
  "ajv": "^8.12.0",
29
29
  "async-lock": "^1.4.0",
30
- "aws-sdk": "^2.1363.0",
30
+ "aws-sdk": "^2.1665.0",
31
31
  "aws4": "^1.9.1",
32
32
  "cookie": "^0.5.0",
33
33
  "crypto-js": "^4.1.1",
@@ -39,12 +39,12 @@
39
39
  "jsontoxml": "^1.0.1",
40
40
  "jsonwebtoken": "^9.0.1",
41
41
  "mongodb": "^3.7.4",
42
- "pako": "^2.1.0",
43
42
  "querystring": "^0.2.0",
44
43
  "readline-sync": "^1.4.10",
45
44
  "socks-proxy-agent": "^8.0.1",
46
45
  "uuid": "^9.0.0",
47
- "xml2js": "^0.6.0"
46
+ "xml2js": "^0.6.0",
47
+ "zlib": "^1.0.5"
48
48
  },
49
49
  "devDependencies": {
50
50
  "chai": "^4.3.7",
Binary file