all files / axway/lib/ util.js

97.37% Statements 111/114
90.41% Branches 66/73
100% Functions 0/0
100% Lines 94/94
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188      539× 44×   12×           19× 19×                   27× 27× 27× 27× 26× 26×     27×               11× 11× 11× 11× 11× 13× 13×   11× 11× 11×                                                                               26× 10×               12× 14×         12× 24× 10× 12× 12×                 12×                
'use strict';
 
const _ = require('lodash');
 
exports.notNull = v => v !== undefined && v !== null;
exports.notEmpty = v => exports.notNull(v) && v.length > 0;
exports.encodeBase64 = s => new Buffer(s, 'utf8').toString('base64');
exports.decodeBase64 = s => new Buffer(s, 'base64').toString('utf8');
 
exports.decodeJson64 = (json64) => {
  if (!exports.notNull(json64)) { return null; }
  let np = exports.decodeBase64(json64);
  try { return JSON.parse(np); } catch (ex) { return null; }
};
 
exports.encodeJson64 = (json) => {
  Iif (!exports.notNull(json)) { return null; }
  return exports.encodeBase64(JSON.stringify(json));
};
 
// Construct a failure object that can be sent to context.fail()
exports.failure = (code, msg, type) => {
  if (!exports.notNull(type)) { type = 'Bad request'; }
  return {
    statusCode: code,
    providerMessage: JSON.stringify(msg),
    errorType: type
  };
};
 
// Construct a path from a curly-bars template (like "/abc/{id}/def"),
// and a lookup function that returns the variable values.
exports.constructPath = (tmpl, varLookup) => {
 
  const re = /([^{]*)(?:{([^}]+)})?/g;
  let o = '';
  let e = null;
  while ((e = re.exec(tmpl)) !== null && (e[1] || e[2])) {
    o += (e[1] || '');
    if (e[2]) { o += (varLookup(e[2]) || ''); }
  }
 
  return o;
};
 
// Given a curly-bars path template (like "/abc/{id}/def") and a path
// that might match it (like "/abc/123/def"), return null if it didn't
// match, or the sub-matches (like {"id": "123"}) if it did.
exports.matchPath = (tmpl, path) => {
  // TODO: improve: this is not particularly stable, or flexible
 
  const re = /([^{]*)(?:{([^}]+)})?/g;
  let o = '^';
  let e = null;
  let vnames = [];
  while ((e = re.exec(tmpl)) !== null && (e[1] || e[2])) {
    o += (e[1] || '');
    if (e[2]) { o += '([^/]*)'; vnames.push(e[2]); }
  }
  let rre = RegExp(o);
  let eResult = null;
  if ((eResult = rre.exec(path)) !== null) {
    let values = {};
    for (let i = 0; i < vnames.length; ++i) {
      values[vnames[i]] = eResult[i + 1];
    }
    return values;
  } else {
    return null;
  }
};
 
// Constructs a context for use by an API Gateway.
exports.apiGateway = (context) => {
  return _.assign(_.clone(context), {
    succeed: (data) => context.succeed(apiGatewayResponse(data)),
    fail: (err) => context.fail(apiGatewayError(err, context)),
    done: (err, data) =>
      context.done(apiGatewayError(err, context), apiGatewayResponse(data))
  });
};
 
// Morph the response object into something the API Gateway can use.
const apiGatewayResponse = (resp) => {
  if (!exports.notNull(resp)) return null;
  let headers = resp.headers || {};
  let resposeData;
  if (exports.notNull(headers["Elements-Next-Page-Token"]) ||
      exports.notNull(headers["Elements-Returned-Count"])) {
    resposeData = {
      data: resp.body,
      nextContinuationToken: headers["Elements-Next-Page-Token"],
      returnedCount: headers["Elements-Returned-Count"]
    };
  } else if(_.isArray(resp.body)) {
    resposeData = {
      data: resp.body,
      returnedCount: resp.body.length
    };
  } else {
    resposeData = resp.body;
  }
 
  return {
    resposeData: resposeData,
    resposeHeaders: resp.headers,
    status: resp.status
  };
};
 
// Morph the failure object into something that an API Gateway can use.
const apiGatewayError = (err, context) => {
  if (!exports.notNull(err)) return null;
  if (err.providerMessage && err.providerMessage.length > 1000) {
    err.providerMessage = err.providerMessage.substr(0, 997) + '...';
  }
  return JSON.stringify({
    httpStatus: determineStatusCode(err.statusCode),
    message: 'Request failed',
    providerMessage: err.providerMessage,
    errorType: err.errorType,
    requestId: context.awsRequestId
  });
};
 
exports.getDeepPropValue = (needle, haystack) => {
  let value;
 
  Object.keys(haystack).forEach( (key) => {
    if(!exports.notNull(value)) {
      if(key === needle) {
        value = haystack[needle];
      } else if(typeof haystack[key] === 'object') {
        value = exports.getDeepPropValue(needle, haystack[key]);
      }
    }
  });
 
  return value;
};
 
exports.tokenRepl =  (o, tokens) => {
 
  const subst = (orig, toks) => {
    if (orig.startsWith('{') && orig.indexOf('}') === orig.length - 1) {
      let k = orig.substr(1, orig.length - 2);
      let val = _.get(toks, k);
      // If val is still undefined, try checking in the body
      if(!exports.notNull(val)) { val = _.get(toks, `body.${k}`); }
      Iif(k === 'pageSize' && (!exports.notNull(val) || isNaN(parseFloat(val)))) { val = 10; } // Add a default pageSize
      if (exports.notNull(val)) { return val; }
      else { return null; }
    }
    return orig.replace(/{([^}]*)}/g, (m, k) => toks[k]);
  };
 
  const repl = o => {
    if (_.isString(o)) { return subst(o, tokens); }
    else if (_.isObject(o)) {
      return _.each(o, (v, k) => {
        const repd = repl(v);
        if (exports.notNull(repd)) o[k] = repd;
         else {
            if (_.isArray(o)) {
              o.splice(k, 1);
            } else {
              delete o[k];
            }
          }
      });
    }
    else { return o; }
  };
 
  return repl(o);
};
 
 
const determineStatusCode = (code) => {
  const acceptableErrorsCodes = ['200', '400', '401', '403', '404', '405', '409', '500', '502'];
 
  if(acceptableErrorsCodes.indexOf('' + code) !== -1) {
    return '' + code;
  } else {
    return ('' + code).charAt(0) + '00';
  }
};