UNPKG

mailchimp-node

Version:
214 lines (181 loc) 5.78 kB
'use strict'; var Buffer = require('safe-buffer').Buffer; // var qs = require('qs'); var crypto = require('crypto'); var hasOwn = {}.hasOwnProperty; var isPlainObject = require('lodash.isplainobject'); var OPTIONS_KEYS = ['api_key', 'idempotency_key', /* 'stripe_account', */'mailchimp_version']; var utils = module.exports = { isAuthKey: function(key) { return typeof key == 'string' && /^(?:[a-z]{2}_)?[A-z0-9]{32}$/.test(key); }, isOptionsHash: function(o) { return isPlainObject(o) && OPTIONS_KEYS.some(function(key) { return hasOwn.call(o, key); }); }, /** * Stringifies an Object */ stringifyRequestData: function(data) { return JSON.stringify(data); }, /** * Outputs a new function with interpolated object property values. * Use like so: * var fn = makeURLInterpolator('some/url/{param1}/{param2}'); * fn({ param1: 123, param2: 456 }); // => 'some/url/123/456' */ makeURLInterpolator: (function() { var rc = { '\n': '\\n', '\"': '\\\"', '\u2028': '\\u2028', '\u2029': '\\u2029', }; return function makeURLInterpolator(str) { var cleanString = str.replace(/["\n\r\u2028\u2029]/g, function($0) { return rc[$0]; }); return function(outputs) { return cleanString.replace(/\{([\s\S]+?)\}/g, function($0, $1) { return encodeURIComponent(outputs[$1] || ''); }); }; }; }()), /** * Return the data argument from a list of arguments */ getDataFromArgs: function(args) { if (args.length < 1 || !isPlainObject(args[0])) { return {}; } if (!utils.isOptionsHash(args[0])) { return args.shift(); } var argKeys = Object.keys(args[0]); var optionKeysInArgs = argKeys.filter(function(key) { return OPTIONS_KEYS.indexOf(key) > -1; }); // In some cases options may be the provided as the first argument. // Here we're detecting a case where there are two distinct arguments // (the first being args and the second options) and with known // option keys in the first so that we can warn the user about it. if (optionKeysInArgs.length > 0 && optionKeysInArgs.length !== argKeys.length) { emitWarning( 'Options found in arguments (' + optionKeysInArgs.join(', ') + '). Did you mean to pass an options ' + 'object? See https://github.com/stripe/stripe-node/wiki/Passing-Options.' ); } return {}; }, /** * Return the options hash from a list of arguments */ getOptionsFromArgs: function(args) { var opts = { auth: null, headers: {}, } if (args.length > 0) { var arg = args[args.length - 1]; if (utils.isAuthKey(arg)) { opts.auth = args.pop(); } else if (utils.isOptionsHash(arg)) { var params = args.pop(); var extraKeys = Object.keys(params).filter(function(key) { return OPTIONS_KEYS.indexOf(key) == -1; }); if (extraKeys.length) { emitWarning('Invalid options found (' + extraKeys.join(', ') + '); ignoring.'); } if (params.api_key) { opts.auth = params.api_key; } if (params.idempotency_key) { opts.headers['Idempotency-Key'] = params.idempotency_key; } // if (params.stripe_account) { // opts.headers['Stripe-Account'] = params.stripe_account; // } if (params.mailchimp_version) { opts.headers['Mailchimp-Version'] = params.mailchimp_version; } } } return opts; }, /** * Provide simple "Class" extension mechanism */ protoExtend: function(sub) { var Super = this; var Constructor = hasOwn.call(sub, 'constructor') ? sub.constructor : function() { Super.apply(this, arguments); }; // This initialization logic is somewhat sensitive to be compatible with // divergent JS implementations like the one found in Qt. See here for more // context: // // https://github.com/stripe/stripe-node/pull/334 Object.assign(Constructor, Super); Constructor.prototype = Object.create(Super.prototype); Object.assign(Constructor.prototype, sub); return Constructor; }, /** * Convert an array into an object with integer string attributes */ arrayToObject: function(arr) { if (Array.isArray(arr)) { var obj = {}; arr.map(function(item, i) { obj[i.toString()] = item; }); return obj; } return arr; }, /** * Secure compare, from https://github.com/freewil/scmp */ secureCompare: function(a, b) { a = Buffer.from(a); b = Buffer.from(b); // return early here if buffer lengths are not equal since timingSafeEqual // will throw if buffer lengths are not equal if (a.length !== b.length) { return false; } // use crypto.timingSafeEqual if available (since Node.js v6.6.0), // otherwise use our own scmp-internal function. if (crypto.timingSafeEqual) { return crypto.timingSafeEqual(a, b); } var len = a.length; var result = 0; for (var i = 0; i < len; ++i) { result |= a[i] ^ b[i]; } return result === 0; }, /** * Remove empty values from an object */ removeEmpty: function(obj) { if (typeof obj !== 'object') { throw new Error('Argument must be an object'); } Object.keys(obj).forEach(function(key) { if (obj[key] === null || obj[key] === undefined) { delete obj[key]; } }); return obj; }, }; function emitWarning(warning) { if (typeof process.emitWarning !== 'function') { return console.warn('Stripe: ' + warning); /* eslint-disable-line no-console */ } return process.emitWarning(warning, 'Stripe'); }