UNPKG

plivo

Version:

A Node.js SDK to make voice calls and send SMS using Plivo and to generate Plivo XML

428 lines (374 loc) 16 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; exports.Axios = Axios; var _exceptions = require('../utils/exceptions'); var Exceptions = _interopRequireWildcard(_exceptions); var _lodash = require('lodash'); var _ = _interopRequireWildcard(_lodash); var _axios = require('axios'); var _axios2 = _interopRequireDefault(_axios); var _querystring = require('querystring'); var _querystring2 = _interopRequireDefault(_querystring); var _http = require('http'); var _http2 = _interopRequireDefault(_http); var _https = require('https'); var _https2 = _interopRequireDefault(_https); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } var HttpsProxyAgent = require('https-proxy-agent'); var httpAgent = new _http2.default.Agent({ keepAlive: true, keepAliveMsecs: 1000, maxSockets: 100, // Maximum sockets per host - requests queue when limit reached maxFreeSockets: 20, // Maximum free sockets per host (increased proportionally) timeout: 60000, // Socket timeout - requests fail if this timeout is reached freeSocketTimeout: 30000 // Free socket timeout }); var httpsAgent = new _https2.default.Agent({ keepAlive: true, keepAliveMsecs: 1000, maxSockets: 100, // Requests queue when limit reached, don't fail immediately maxFreeSockets: 20, // Increased proportionally with maxSockets timeout: 60000, // Only fail if timeout reached while waiting for socket freeSocketTimeout: 30000 }); // Set default agents for axios _axios2.default.defaults.httpAgent = httpAgent; _axios2.default.defaults.httpsAgent = httpsAgent; function Axios(config) { var auth = 'Basic ' + new Buffer(config.authId + ':' + config.authToken).toString('base64'); var headers = { Authorization: auth, 'User-Agent': config.userAgent, 'Content-Type': 'application/json' }; var isGeoPermissionError = function isGeoPermissionError(response) { var code = response.status; var url = response.config.url; var method = response.config.method; var GeoPermissionEndpoints = ['/Message/', '/Call/', '/Session/']; return code == 403 && method.toLowerCase() == "post" && GeoPermissionEndpoints.some(function (endpoint) { return url.endsWith(endpoint); }); }; var retryWrapper = function retryWrapper(axiosInstance, options) { var max_time = options.retryTime; var counter = 0; // Create a dedicated axios instance for this request to avoid global interceptor pollution var requestAxios = _axios2.default.create({ httpAgent: axiosInstance.defaults.httpAgent, httpsAgent: axiosInstance.defaults.httpsAgent, timeout: axiosInstance.defaults.timeout }); // Add interceptor with proper cleanup var interceptorId = requestAxios.interceptors.response.use(null, function (error) { var config = error.config; if (counter < max_time && error.response && error.response.status >= 500) { counter++; config.url = options.urls[counter] + options.authId + '/' + options.action; return new Promise(function (resolve) { resolve(requestAxios(config)); }); } return Promise.reject(error); }); return { axios: requestAxios, cleanup: function cleanup() { // Clean up the interceptor when done requestAxios.interceptors.response.eject(interceptorId); } }; }; return function (method, action, params) { var configuration = config; // Add support fot multipart requests. if (typeof params != 'undefined' && params.multipart == true) { return new Promise(function (resolve, reject) { delete params.multipart; var FormData = require('form-data'); var multipartParams = new FormData(); for (var key in params) { if (key != 'file') { multipartParams.append(key, params[key]); } else { // In case files are in array if (Array.isArray(params.file)) { for (var index = 0; index < params.file.length; index++) { multipartParams.append(key, require('fs').createReadStream(params.file[index])); } } else { multipartParams.append(key, require('fs').createReadStream(params[key])); } } } var headers = multipartParams.getHeaders(); var config = { method: method, url: configuration.url + '/' + action, headers: { 'Authorization': 'Basic ' + new Buffer(configuration.authId + ':' + configuration.authToken).toString('base64'), 'User-Agent': configuration.userAgent, 'content-type': headers['content-type'] }, data: multipartParams }; (0, _axios2.default)(config).then(function (response) { var exceptionClass = { 400: Exceptions.InvalidRequestError, 401: Exceptions.AuthenticationError, 404: Exceptions.ResourceNotFoundError, 405: Exceptions.InvalidRequestError, 406: Exceptions.NotAcceptableError, 500: Exceptions.ServerError }[response.status] || Error; if (isGeoPermissionError(response)) { exceptionClass = Exceptions.GeoPermissionError; } if (!_.inRange(response.status, 200, 300)) { var body = response.data; if ((typeof body === 'undefined' ? 'undefined' : _typeof(body)) === 'object') { reject(new exceptionClass(JSON.stringify(body))); } else { reject(new exceptionClass(body)); } } resolve({ response: response, body: response.data }); }).catch(function (error) { //client side exception like file not found case if (error.response == undefined) { return reject(error.stack); } var exceptionClass = { 400: Exceptions.InvalidRequestError, 401: Exceptions.AuthenticationError, 404: Exceptions.ResourceNotFoundError, 405: Exceptions.InvalidRequestError, 406: Exceptions.NotAcceptableError, 500: Exceptions.ServerError }[error.response.status] || Error; if (isGeoPermissionError(error.response)) { exceptionClass = Exceptions.GeoPermissionError; } if (!_.inRange(error.response.status, 200, 300)) { var body = error.response.data; if ((typeof body === 'undefined' ? 'undefined' : _typeof(body)) === 'object') { reject(new exceptionClass(error)); } else { if (error.response.status >= 500) { reject(new Exceptions.ServerError(error)); } reject(new exceptionClass(error)); } } reject(error.stack + '\n' + JSON.stringify(error.response.data)); }); }); } if (typeof params != 'undefined' && typeof params.file != 'undefined') { var files = []; if (Array.isArray(params.file)) { for (var index = 0; index < params.file.length; index++) { files[index] = require('fs').createReadStream(params.file[index]); } } else { files[0] = require('fs').createReadStream(params.file); } params.file = files; } var options = { url: config.url + '/' + action, method: method, data: params || '', headers: headers, json: true }; var apiVoiceUris = ['https://api.plivo.com/v1/Account/', 'https://api.plivo.com/v1/Account/', 'https://api.plivo.com/v1/Account/']; var isVoiceReq = false; if (params) { if (params.hasOwnProperty('is_call_insights_request')) { options.url = params.call_insights_base_url + params.call_insights_request_path; delete params.is_call_insights_request; delete params.call_insights_base_url; delete params.call_insights_request_path; delete options.data; options.json = params; } else if (params.hasOwnProperty('is_voice_request')) { options.url = apiVoiceUris[0] + config.authId + '/' + action; delete params.is_voice_request; isVoiceReq = true; } else if (params.hasOwnProperty('override_url')) { // currently used by Lookup API but is generic enough to be used // by any product in future. options.url = params.override_url; delete params.override_url; } } if (method === 'GET' && options.data !== '' && !isVoiceReq) { var query = '?' + _querystring2.default.stringify(params); options.url += query; delete options.data; } if (typeof config.proxy !== 'undefined') { // Create proxy agent with connection pooling settings options.httpsAgent = new HttpsProxyAgent(config.proxy, { keepAlive: true, keepAliveMsecs: 1000, maxSockets: 100, // Match the main configuration maxFreeSockets: 20, // Match the main configuration timeout: 60000, freeSocketTimeout: 30000 }); } if (typeof config.timeout !== 'undefined') { options.timeout = config.timeout; } else if (typeof config.timeout === 'undefined' && isVoiceReq) { options.timeout = 5000; } return new Promise(function (resolve, reject) { if (isVoiceReq) { // Set up retry wrapper with proper cleanup var retrySetup = retryWrapper(_axios2.default, { retryTime: 2, urls: apiVoiceUris, authId: config.authId, action: action }); var retryAxios = retrySetup.axios; options.url = apiVoiceUris[0] + config.authId + '/' + action; if (method === 'GET' && options.data !== '') { var _query = '?' + _querystring2.default.stringify(params); options.url += _query; delete options.data; } retryAxios(options).then(function (response) { var exceptionClass = { 400: Exceptions.InvalidRequestError, 401: Exceptions.AuthenticationError, 404: Exceptions.ResourceNotFoundError, 405: Exceptions.InvalidRequestError, 406: Exceptions.NotAcceptableError, 500: Exceptions.ServerError, 409: Exceptions.InvalidRequestError, 422: Exceptions.InvalidRequestError, 207: Exceptions.InvalidRequestError }[response.status] || Error; if (isGeoPermissionError(response)) { exceptionClass = Exceptions.GeoPermissionError; } if (!_.inRange(response.status, 200, 300)) { var body = response.data; if ((typeof body === 'undefined' ? 'undefined' : _typeof(body)) === 'object') { reject(new exceptionClass(JSON.stringify(body))); } else { reject(new exceptionClass(body)); } } resolve({ response: response, body: response.data }); }).catch(function (error) { if (error.response == undefined) { return reject(error.stack); } var exceptionClass = { 400: Exceptions.InvalidRequestError, 401: Exceptions.AuthenticationError, 404: Exceptions.ResourceNotFoundError, 405: Exceptions.InvalidRequestError, 406: Exceptions.NotAcceptableError, 500: Exceptions.ServerError, 409: Exceptions.InvalidRequestError, 422: Exceptions.InvalidRequestError, 207: Exceptions.InvalidRequestError }[error.response.status] || Error; if (isGeoPermissionError(error.response)) { exceptionClass = Exceptions.GeoPermissionError; } if (!_.inRange(error.response.status, 200, 300)) { var body = error.response.data; if ((typeof body === 'undefined' ? 'undefined' : _typeof(body)) === 'object') { reject(new exceptionClass(error)); } else { if (error.response.status >= 500) { reject(new Exceptions.ServerError(error)); } reject(new exceptionClass(error)); } } reject(error.stack + '\n' + JSON.stringify(error.response.data)); }).finally(function () { // Clean up interceptors in all cases - success, error, or unexpected exception retrySetup.cleanup(); }); } else { (0, _axios2.default)(options).then(function (response) { var exceptionClass = { 400: Exceptions.InvalidRequestError, 401: Exceptions.AuthenticationError, 404: Exceptions.ResourceNotFoundError, 405: Exceptions.InvalidRequestError, 406: Exceptions.NotAcceptableError, 500: Exceptions.ServerError, 409: Exceptions.InvalidRequestError, 422: Exceptions.InvalidRequestError, 207: Exceptions.InvalidRequestError }[response.status] || Error; if (isGeoPermissionError(response)) { exceptionClass = Exceptions.GeoPermissionError; } if (!_.inRange(response.status, 200, 300)) { var body = response.data; if ((typeof body === 'undefined' ? 'undefined' : _typeof(body)) === 'object') { reject(new exceptionClass(JSON.stringify(body))); } else { reject(new exceptionClass(body)); } } else { var _body2 = response.data; var isObj = (typeof _body === 'undefined' ? 'undefined' : _typeof(_body)) === 'object' && _body !== null && !(_body instanceof Array) && !(_body instanceof Date); if (isObj) { _body['statusCode'] = response.status; } resolve({ response: response, body: _body2 }); } }).catch(function (error) { if (error.response == undefined) { return reject(error.stack); } var exceptionClass = { 400: Exceptions.InvalidRequestError, 401: Exceptions.AuthenticationError, 404: Exceptions.ResourceNotFoundError, 405: Exceptions.InvalidRequestError, 406: Exceptions.NotAcceptableError, 500: Exceptions.ServerError, 409: Exceptions.InvalidRequestError, 422: Exceptions.InvalidRequestError, 207: Exceptions.InvalidRequestError }[error.response.status] || Error; if (isGeoPermissionError(error.response)) { exceptionClass = Exceptions.GeoPermissionError; } if (!_.inRange(error.response.status, 200, 300)) { var body = error.response.data; if ((typeof body === 'undefined' ? 'undefined' : _typeof(body)) === 'object') { reject(new exceptionClass(error)); } else { if (error.response.status >= 500) { reject(new Exceptions.ServerError(error)); } reject(new exceptionClass(error)); } } reject(error.stack + '\n' + JSON.stringify(error.response.data)); }); } }); }; }