diff --git a/lib/client.js b/lib/client.js index b1eb7e4..b768258 100644 --- a/lib/client.js +++ b/lib/client.js @@ -11,21 +11,21 @@ * @author Samuel Fortier-Galarneau */ -'use strict'; +"use strict"; -var util = require('util'); -var request = require('request'); -var url = require('url'); -var events = require('events'); +var util = require("util"); +var request = require("request"); +var url = require("url"); +var events = require("events"); -var WebSocket = require('ws'); -var swagger = require('swagger-client'); -var Promise = require('bluebird'); +var WebSocket = require("ws"); +var swagger = require("swagger-client"); +var Promise = require("bluebird"); -var _ = require('lodash'); -var backoff = require('backoff-func'); -var _resources = require('./resources.js'); -var _utils = require('./utils.js'); +var _ = require("lodash"); +var backoff = require("backoff-func"); +var _resources = require("./resources.js"); +var _utils = require("./utils.js"); /** * Client is an Event Emitter that allows root level resources @@ -60,10 +60,8 @@ function Client(baseUrl, user, pass) { protocol: parsedUrl.protocol, host: parsedUrl.host, hostname: parsedUrl.hostname, - // support optional path prefix in asterisk http.conf - prefix: parsedUrl.pathname === '/' ? '' : parsedUrl.pathname, user: user, - pass: pass + pass: pass, }; // Keep track of instance event listeners. once true means that the callback @@ -89,13 +87,11 @@ util.inherits(Client, events.EventEmitter); * @returns {Q} promise - a promise that will resolve to a client */ Client.prototype._attachApi = function () { - var self = this; - return new Promise(function(resolve, reject) { - + return new Promise(function (resolve, reject) { swagger.authorizations.add( - 'basic-auth', + "basic-auth", new swagger.PasswordAuthorization( self._connection.hostname, self._connection.user, @@ -105,24 +101,22 @@ Client.prototype._attachApi = function () { // Connect to API using swagger and attach resources on Client instance var ariUrl = util.format( - '%s//%s%s/ari/api-docs/resources.json', + "%s//%s/ari/api-docs/resources.json", self._connection.protocol, - self._connection.host, - self._connection.prefix + self._connection.host ); request(ariUrl, function (err) { - if (err && - ['ETIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED'].indexOf(err.code) !== -1) { - err.name = 'HostIsNotReachable'; + if (err && ["ETIMEDOUT", "ENOTFOUND", "ECONNREFUSED"].indexOf(err.code) !== -1) { + err.name = "HostIsNotReachable"; - self.emit('APILoadError', err); + self.emit("APILoadError", err); reject(err); } else { self._swagger = new swagger.SwaggerApi({ url: ariUrl, success: swaggerLoaded, - failure: swaggerFailed + failure: swaggerFailed, }); } }); @@ -135,8 +129,8 @@ Client.prototype._attachApi = function () { * @memberof module:ari-client~Client~_attachApi * @private */ - function swaggerLoaded () { - if(self._swagger.ready === true) { + function swaggerLoaded() { + if (self._swagger.ready === true) { // Attach resources to client _.each(_.keys(self._swagger.apis), attachResource); @@ -155,8 +149,8 @@ Client.prototype._attachApi = function () { * @memberof module:ari-client~Client~_attachApi * @private */ - function swaggerFailed (err) { - self.emit('APILoadError', err); + function swaggerFailed(err) { + self.emit("APILoadError", err); reject(err); } @@ -175,7 +169,7 @@ Client.prototype._attachApi = function () { * @param {string} resourceType - the type of the resource to setup a * create method for */ - function attachResourceCreators (resourceType) { + function attachResourceCreators(resourceType) { self[resourceType] = function (id, values) { return _resources[resourceType](self, id, values); }; @@ -190,13 +184,13 @@ Client.prototype._attachApi = function () { * @private * @param {string} resource - the name of the resource */ - function attachResource (resource) { + function attachResource(resource) { self[resource] = {}; var operations = self._swagger.apis[resource].operations; _.each(_.keys(operations), attachOperation); // Attach operation to resource - function attachOperation (operation) { + function attachOperation(operation) { self[resource][operation] = callSwagger; var oper = self._swagger.apis[resource].operations[operation]; @@ -220,18 +214,23 @@ Client.prototype._attachApi = function () { * @param {Function} callback - callback invoked with swagger response * @returns {Q} promise - a promise that will resolve to a client */ - function callSwagger (/* args..., callback */) { + function callSwagger(/* args..., callback */) { + var startTime = Date.now(); var args = _.toArray(arguments); + if (self.onRequest) self.onRequest(resource, operation, args); // Separate user callback from other args var options = _.first(args); - var userCallback = (_.isFunction(_.last(args))) ? _.last(args) - : undefined; + var userCallback = _.isFunction(_.last(args)) ? _.last(args) : undefined; - return new Promise(function(innerResolve, innerReject) { + return new Promise(function (innerResolve, innerReject) { args = []; - if (options === undefined || options === null || - _.isFunction(options) || _.isArray(options)) { + if ( + options === undefined || + options === null || + _.isFunction(options) || + _.isArray(options) + ) { options = {}; } else { // Swagger can alter options passed in @@ -251,9 +250,12 @@ Client.prototype._attachApi = function () { self._swagger.apis[resource][operation].apply(null, args); // Handle error from Swagger - function swaggerError (err) { + function swaggerError(err) { + var endTime = Date.now(); + var duration = endTime - startTime; + if (self.onError) self.onError(resource, operation, duration); if (err && err.data) { - err = new Error(err.data.toString('utf-8')); + err = new Error(err.data.toString("utf-8")); } innerReject(err); @@ -269,33 +271,31 @@ Client.prototype._attachApi = function () { * @private * @param {Object} response - response from swagger */ - function processResponse (response) { + function processResponse(response) { + var endTime = Date.now(); + var duration = endTime - startTime; + if (self.onResponse) self.onResponse(resource, operation, duration, response); var result; - if (respType === 'binary') { + if (respType === "binary") { result = Buffer.from(response.data); } else { - result = response.data.toString('utf-8'); + result = response.data.toString("utf-8"); if (respType !== null && result) { result = JSON.parse(result); } } - if (_.includes(_resources.knownTypes, respType) && - _resources[respType] !== undefined) { - + if ( + _.includes(_resources.knownTypes, respType) && + _resources[respType] !== undefined + ) { if (multi) { result = _.map(result, function (obj) { - return _resources[respType]( - self, - obj - ); + return _resources[respType](self, obj); }); } else { - result = _resources[respType]( - self, - result - ); + result = _resources[respType](self, result); } } @@ -320,36 +320,34 @@ Client.prototype.start = function (apps, subscribeAll, callback) { var processingError = false; // Perform argument renaming for backwards compatibility - if (typeof subscribeAll === 'function') { + if (typeof subscribeAll === "function") { callback = subscribeAll; subscribeAll = null; } - return new Promise(function(resolve, reject) { - + return new Promise(function (resolve, reject) { // Rewrite resolve/reject functions so they can only be called once and // each disables the other when called. resolve = _.once(resolve); reject = _.once(reject); - var applications = (_.isArray(apps)) ? apps.join(',') : apps; + var applications = _.isArray(apps) ? apps.join(",") : apps; var wsUrl = util.format( - '%s://%s%s/ari/events?app=%s&api_key=%s:%s', - (self._connection.protocol === 'https:' ? 'wss' : 'ws'), + "%s://%s/ari/events?app=%s&api_key=%s:%s", + self._connection.protocol === "https:" ? "wss" : "ws", self._connection.host, - self._connection.prefix, applications, encodeURIComponent(self._connection.user), encodeURIComponent(self._connection.pass) ); if (subscribeAll) { - wsUrl += '&subscribeAll=true'; + wsUrl += "&subscribeAll=true"; } var retry = backoff.create({ - delay: 100 + delay: 100, }); connect(); @@ -361,16 +359,16 @@ Client.prototype.start = function (apps, subscribeAll, callback) { * @memberof module:ari-client~Client~start * @private */ - function connect () { + function connect() { self._ws = new WebSocket(wsUrl); - self._ws.on('open', function () { + self._ws.on("open", function () { processOpen(); }); - self._ws.on('error', processError); - self._ws.on('message', processMessage); - self._ws.on('pong', processPong); - self._ws.on('close', processClose); + self._ws.on("error", processError); + self._ws.on("message", processMessage); + self._ws.on("pong", processPong); + self._ws.on("close", processClose); } /** @@ -380,8 +378,8 @@ Client.prototype.start = function (apps, subscribeAll, callback) { * @memberof module:ari-client~Client~start * @private */ - function processPong () { - self.emit('pong'); + function processPong() { + self.emit("pong"); } /** @@ -393,7 +391,7 @@ Client.prototype.start = function (apps, subscribeAll, callback) { * @param {Object} msg - the web socket message * @param {Object} flags - web socket control flags */ - function processMessage (msg, flags) { + function processMessage(msg, flags) { var event = {}; if (msg) { event = JSON.parse(msg); @@ -407,14 +405,12 @@ Client.prototype.start = function (apps, subscribeAll, callback) { // Pass in any property that is a known type as an object _.each(eventModel.properties, function (prop) { - if (_.includes(_resources.knownTypes, prop.dataType) && - event[prop.name] !== undefined && - _resources[prop.dataType] !== undefined) { - - var instance = _resources[prop.dataType]( - self, - event[prop.name] - ); + if ( + _.includes(_resources.knownTypes, prop.dataType) && + event[prop.name] !== undefined && + _resources[prop.dataType] !== undefined + ) { + var instance = _resources[prop.dataType](self, event[prop.name]); resources[prop.name] = instance; // Keep track of which instance specific events we should @@ -453,16 +449,12 @@ Client.prototype.start = function (apps, subscribeAll, callback) { resources = undefined; } - self.emit('*', event, resources); + self.emit("*", event, resources); self.emit(event.type, event, resources); // If appropriate, emit instance specific events if (instanceIds.length > 0) { _.each(instanceIds, function (instanceId) { - self.emit( - util.format('%s-%s', event.type, instanceId), - event, - resources - ); + self.emit(util.format("%s-%s", event.type, instanceId), event, resources); }); } } @@ -474,13 +466,13 @@ Client.prototype.start = function (apps, subscribeAll, callback) { * @memberof module:ari-client~Client~start * @private */ - function processOpen () { + function processOpen() { processingError = false; // reset backoff handler when we successfully connect retry = backoff.create({ - delay: 100 + delay: 100, }); - self.emit('WebSocketConnected'); + self.emit("WebSocketConnected"); // onced, will not be called when an automatic reconnect succeeds. resolve(); } @@ -495,7 +487,7 @@ Client.prototype.start = function (apps, subscribeAll, callback) { * @param {Number} reason - reason code for disconnect * @param {String} description - reason text for disconnect */ - function processClose (reason, description) { + function processClose(reason, description) { // was connection closed on purpose? if (self._wsClosed) { self._wsClosed = false; @@ -516,7 +508,7 @@ Client.prototype.start = function (apps, subscribeAll, callback) { * @private * @param {Error} err - error object */ - function processError (err) { + function processError(err) { // was connection closed on purpose? if (self._wsClosed) { return; @@ -537,19 +529,17 @@ Client.prototype.start = function (apps, subscribeAll, callback) { */ function reconnect(err) { var scheduled = retry(connect); - var msg = err ? err.message : 'unknown'; + var msg = err ? err.message : "unknown"; if (!scheduled) { // onced or disabled if initial connection succeeds. - reject(new Error('Connection attempts exceeded WebSocketMaxRetries. ' + - msg)); + reject(new Error("Connection attempts exceeded WebSocketMaxRetries. " + msg)); - self.emit('WebSocketMaxRetries', err); + self.emit("WebSocketMaxRetries", err); } else { - self.emit('WebSocketReconnecting', err); + self.emit("WebSocketReconnecting", err); } } - }).asCallback(callback); }; @@ -594,17 +584,27 @@ Client.prototype.ping = function () { * The callback to be called upon connection * @returns {Q} promise - a promise that will resolve to a client */ -module.exports.connect = function (baseUrl, user, pass, - /** - * @callback connectCallback - * @memberof module:ari-client - * @param {Error} err - error object if any, null otherwise - * @param {Client} ari - ARI client instance - */ - callback) { - +module.exports.connect = function ( + baseUrl, + user, + pass, + onRequest, + onResponse, + onError, + /** + * @callback connectCallback + * @memberof module:ari-client + * @param {Error} err - error object if any, null otherwise + * @param {Client} ari - ARI client instance + */ + callback +) { var client = new Client(baseUrl, user, pass); client.setMaxListeners(0); + client.onRequest = onRequest; + client.onResponse = onResponse; + client.onError = onError; + return client._attachApi().asCallback(callback); }; diff --git a/lib/resources.js b/lib/resources.js index 5f1901f..048bafe 100644 --- a/lib/resources.js +++ b/lib/resources.js @@ -9,29 +9,29 @@ * @author Samuel Fortier-Galarneau */ -'use strict'; +"use strict"; -var util = require('util'); +var util = require("util"); -var _ = require('lodash'); -var uuid = require('uuid'); -var Promise = require('bluebird'); +var _ = require("lodash"); +var uuid = require("uuid"); +var Promise = require("bluebird"); -var _utils = require('./utils.js'); +var _utils = require("./utils.js"); // List of known resources to instantiate as first class object var knownTypes = [ - 'Application', - 'Asterisk', - 'Channel', - 'Bridge', - 'DeviceState', - 'Endpoint', - 'LiveRecording', - 'Mailbox', - 'Playback', - 'Sound', - 'StoredRecording' + "Application", + "Asterisk", + "Channel", + "Bridge", + "DeviceState", + "Endpoint", + "LiveRecording", + "Mailbox", + "Playback", + "Sound", + "StoredRecording", ]; // Used to convert lists of resources to lists of objects @@ -48,7 +48,7 @@ var swaggerListTypeRegex = /List\[([A-Za-z]+)\]/; * @prop {Client} _client - ARI client instance * @prop {string} id - resource identifier */ -function Resource (client, id, objValues) { +function Resource(client, id, objValues) { var self = this; self._client = client; @@ -95,16 +95,17 @@ Resource.prototype.generateId = function () { * @param {string} event - event name * @param {onCallback} callback - callback invoked on event */ -Resource.prototype.on = function (event, - /** - * @callback onCallback - * @memberof module:resources~Resource - * @param {Object} event - full event object - * @param {Object} instance(s) - single resource instance or object of - * resource instances with callable operations attached - */ - callback) { - +Resource.prototype.on = function ( + event, + /** + * @callback onCallback + * @memberof module:resources~Resource + * @param {Object} event - full event object + * @param {Object} instance(s) - single resource instance or object of + * resource instances with callable operations attached + */ + callback +) { var self = this; var id = self._id() && self._id().toString(); var listeners = self._client._instanceListeners; @@ -114,8 +115,8 @@ Resource.prototype.on = function (event, listeners[event] = []; } - listeners[event].push({once: false, id: id, callback: callback}); - self._client.on(util.format('%s-%s', event, id), callback); + listeners[event].push({ once: false, id: id, callback: callback }); + self._client.on(util.format("%s-%s", event, id), callback); }; /** @@ -126,16 +127,17 @@ Resource.prototype.on = function (event, * @param {string} event - event name * @param {onceCallback} callback - callback invoked on event */ -Resource.prototype.once = function (event, - /** - * @callback onceCallback - * @memberof module:resources~Resource - * @param {Object} event - full event object - * @param {Object} instance(s) - single resource instance or object of - * resource instances with callable operations attached - */ - callback) { - +Resource.prototype.once = function ( + event, + /** + * @callback onceCallback + * @memberof module:resources~Resource + * @param {Object} event - full event object + * @param {Object} instance(s) - single resource instance or object of + * resource instances with callable operations attached + */ + callback +) { var self = this; var id = self._id() && self._id().toString(); var listeners = self._client._instanceListeners; @@ -145,8 +147,8 @@ Resource.prototype.once = function (event, listeners[event] = []; } - listeners[event].push({once: true, id: id, callback: callback}); - self._client.once(util.format('%s-%s', event, id), callback); + listeners[event].push({ once: true, id: id, callback: callback }); + self._client.once(util.format("%s-%s", event, id), callback); }; /** @@ -174,10 +176,10 @@ Resource.prototype.removeListener = function (event, callback) { // unregister event for this instance if (listeners[event]) { - var updatedListeners = _.filter(listeners[event], function(listener) { + var updatedListeners = _.filter(listeners[event], function (listener) { return listener.id !== id || listener.callback !== callback; }); - var instanceListeners = _.filter(listeners[event], function(listener) { + var instanceListeners = _.filter(listeners[event], function (listener) { return listener.id === id && listener.callback === callback; }); // if multiple, remove the last listener registered @@ -187,7 +189,7 @@ Resource.prototype.removeListener = function (event, callback) { } self._client._instanceListeners[event] = updatedListeners; - self._client.removeListener(util.format('%s-%s', event, id), callback); + self._client.removeListener(util.format("%s-%s", event, id), callback); } }; @@ -205,12 +207,12 @@ Resource.prototype.removeAllListeners = function (event) { // unregister event for this instance if (listeners[event]) { - var updatedListeners = _.filter(listeners[event], function(listener) { + var updatedListeners = _.filter(listeners[event], function (listener) { return listener.id !== id; }); self._client._instanceListeners[event] = updatedListeners; - self._client.removeAllListeners(util.format('%s-%s', event, id)); + self._client.removeAllListeners(util.format("%s-%s", event, id)); } }; @@ -224,7 +226,7 @@ Resource.prototype.removeAllListeners = function (event) { * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function Application (client, id, objValues) { +function Application(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -253,7 +255,7 @@ Application.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~Application */ -Application.prototype._param = 'applicationName'; +Application.prototype._param = "applicationName"; /** * The name of this resource. @@ -261,7 +263,7 @@ Application.prototype._param = 'applicationName'; * @member {string} _resource * @memberof module:resources~Application */ -Application.prototype._resource = 'applications'; +Application.prototype._resource = "applications"; /** * Asterisk object for asterisk API responses. @@ -272,7 +274,7 @@ Application.prototype._resource = 'applications'; * @param {Client} client - ARI client instance * @param {Object} objValues - ownProperties to copy to the instance */ -function Asterisk (client, objValues) { +function Asterisk(client, objValues) { var self = this; Resource.call(self, client, undefined, objValues); } @@ -285,7 +287,7 @@ util.inherits(Asterisk, Resource); * @member {string} _param * @memberof module:resources~Asterisk */ -Asterisk.prototype._param = ''; +Asterisk.prototype._param = ""; /** * The name of this resource. @@ -293,7 +295,7 @@ Asterisk.prototype._param = ''; * @member {string} _resource * @memberof module:resources~Asterisk */ -Asterisk.prototype._resource = 'asterisk'; +Asterisk.prototype._resource = "asterisk"; /** * Bridge object for bridge API responses. @@ -305,7 +307,7 @@ Asterisk.prototype._resource = 'asterisk'; * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function Bridge (client, id, objValues) { +function Bridge(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -334,14 +336,14 @@ Bridge.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~Bridge */ -Bridge.prototype._param = 'bridgeId'; +Bridge.prototype._param = "bridgeId"; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Bridge */ -Bridge.prototype._resource = 'bridges'; +Bridge.prototype._resource = "bridges"; /** * Used to pass generated ids into create methods. @@ -361,17 +363,17 @@ Bridge.prototype._resource = 'bridges'; */ Bridge.prototype._createMethods = { create: { - param: Bridge.prototype._param + param: Bridge.prototype._param, }, // this is not currently supported in ARI play: { - param: 'playbackId', - requiresInstance: true + param: "playbackId", + requiresInstance: true, }, record: { - param: 'name', - requiresInstance: true - } + param: "name", + requiresInstance: true, + }, }; /** @@ -384,7 +386,7 @@ Bridge.prototype._createMethods = { * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function Channel (client, id, objValues) { +function Channel(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -413,14 +415,14 @@ Channel.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~Channel */ -Channel.prototype._param = 'channelId'; +Channel.prototype._param = "channelId"; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Channel */ -Channel.prototype._resource = 'channels'; +Channel.prototype._resource = "channels"; /** * Used to pass generated ids into create methods. @@ -448,26 +450,26 @@ Channel.prototype._resource = 'channels'; */ Channel.prototype._createMethods = { create: { - param: Channel.prototype._param + param: Channel.prototype._param, }, originate: { - param: Channel.prototype._param + param: Channel.prototype._param, }, snoopChannel: { - param: 'snoopId', - requiresInstance: true + param: "snoopId", + requiresInstance: true, }, play: { - param: 'playbackId', - requiresInstance: true + param: "playbackId", + requiresInstance: true, }, record: { - param: 'name', - requiresInstance: true + param: "name", + requiresInstance: true, }, externalMedia: { - param: Channel.prototype._param - } + param: Channel.prototype._param, + }, }; /** @@ -480,7 +482,7 @@ Channel.prototype._createMethods = { * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function DeviceState (client, id, objValues) { +function DeviceState(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -509,14 +511,14 @@ DeviceState.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~DeviceState */ -DeviceState.prototype._param = 'deviceName'; +DeviceState.prototype._param = "deviceName"; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~DeviceState */ -DeviceState.prototype._resource = 'deviceStates'; +DeviceState.prototype._resource = "deviceStates"; /** * Endpoint object for endpoint API responses. @@ -528,7 +530,7 @@ DeviceState.prototype._resource = 'deviceStates'; * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function Endpoint (client, id, objValues) { +function Endpoint(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -553,8 +555,8 @@ Endpoint.prototype._id = function (value) { tech: self.technology, resource: self.resource, toString: function () { - return util.format('%s/%s', self.technology, self.resource); - } + return util.format("%s/%s", self.technology, self.resource); + }, }; } }; @@ -565,14 +567,14 @@ Endpoint.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~Endpoint */ -Endpoint.prototype._param = ['tech', 'resource']; +Endpoint.prototype._param = ["tech", "resource"]; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Endpoint */ -Endpoint.prototype._resource = 'endpoints'; +Endpoint.prototype._resource = "endpoints"; /** * LiveRecording object for liveRecording API responses. @@ -584,7 +586,7 @@ Endpoint.prototype._resource = 'endpoints'; * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function LiveRecording (client, id, objValues) { +function LiveRecording(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -613,14 +615,14 @@ LiveRecording.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~LiveRecording */ -LiveRecording.prototype._param = 'recordingName'; +LiveRecording.prototype._param = "recordingName"; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~LiveRecording */ -LiveRecording.prototype._resource = 'recordings'; +LiveRecording.prototype._resource = "recordings"; /** * Mailbox object for mailbox API responses. @@ -632,7 +634,7 @@ LiveRecording.prototype._resource = 'recordings'; * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function Mailbox (client, id, objValues) { +function Mailbox(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -661,14 +663,14 @@ Mailbox.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~Mailbox */ -Mailbox.prototype._param = 'mailboxName'; +Mailbox.prototype._param = "mailboxName"; /** * The name of this resource. . * * @member {string} _resource * @memberof module:resources~Mailbox */ -Mailbox.prototype._resource = 'mailboxes'; +Mailbox.prototype._resource = "mailboxes"; /** * Playback object for playback API responses. @@ -680,7 +682,7 @@ Mailbox.prototype._resource = 'mailboxes'; * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function Playback (client, id, objValues) { +function Playback(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -709,14 +711,14 @@ Playback.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~Playback */ -Playback.prototype._param = 'playbackId'; +Playback.prototype._param = "playbackId"; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Playback */ -Playback.prototype._resource = 'playbacks'; +Playback.prototype._resource = "playbacks"; /** * Sound object for sound API responses. @@ -728,7 +730,7 @@ Playback.prototype._resource = 'playbacks'; * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function Sound (client, id, objValues) { +function Sound(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -757,14 +759,14 @@ Sound.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~Sound */ -Sound.prototype._param = 'soundId'; +Sound.prototype._param = "soundId"; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Sound */ -Sound.prototype._resource = 'sounds'; +Sound.prototype._resource = "sounds"; /** * StoredRecording object for storedRecording API responses. @@ -776,7 +778,7 @@ Sound.prototype._resource = 'sounds'; * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ -function StoredRecording (client, id, objValues) { +function StoredRecording(client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } @@ -805,14 +807,14 @@ StoredRecording.prototype._id = function (value) { * @member {string} _param * @memberof module:resources~StoredRecording */ -StoredRecording.prototype._param = 'recordingName'; +StoredRecording.prototype._param = "recordingName"; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~StoredRecording */ -StoredRecording.prototype._resource = 'recordings'; +StoredRecording.prototype._resource = "recordings"; /** * Attach operations to the resource instance bound to this. @@ -822,7 +824,7 @@ StoredRecording.prototype._resource = 'recordings'; * @this module:resources~Resource * @private */ -function attachOperations () { +function attachOperations() { /*jshint validthis:true*/ var self = this; var operations = self._client._swagger.apis[self._resource].operations; @@ -838,9 +840,11 @@ function attachOperations () { * @private * @param {string} operation - the operation to attach */ - function attachOperation (operation) { + function attachOperation(operation) { self[operation] = callSwagger; - + self.onRequest = self._client.onRequest; + self.onResponse = self._client.onResponse; + self.onError = self._client.onError; var oper = self._client._swagger.apis[self._resource].operations[operation]; var respType = oper.type; var multi = false; @@ -863,22 +867,25 @@ function attachOperations () { * purposes of creating the resource through ARI * @param {Function} callback - callback invoked with swagger response */ - function callSwagger (/* args..., callback */) { - + function callSwagger(/* args..., callback */) { // Separate user callback from other args and inject object id(s) // if appropriate + var startTime = Date.now(); var args = _.toArray(arguments); + if (self.onRequest) self.onRequest(self._resource, operation, args); var options = _.first(args); var createInstance = args[1]; - var userCallback = (_.isFunction(_.last(args))) ? _.last(args) - : undefined; - - return new Promise(function(resolve, reject) { + var userCallback = _.isFunction(_.last(args)) ? _.last(args) : undefined; + return new Promise(function (resolve, reject) { args = []; - if (options === undefined || options === null || - _.isFunction(options) || _.isArray(options)) { + if ( + options === undefined || + options === null || + _.isFunction(options) || + _.isArray(options) + ) { options = {}; } else { // Swagger can alter options passed in @@ -899,10 +906,9 @@ function attachOperations () { // Inject parameter using instance value if (expectedParam === actualParam && param.required) { - var identifier = self._id() || ''; + var identifier = self._id() || ""; // In case of multi ids - options[expectedParam] = - identifier[expectedParam] || identifier; + options[expectedParam] = identifier[expectedParam] || identifier; } }); @@ -912,9 +918,7 @@ function attachOperations () { var createMethod = self._createMethods[operation]; if (createMethod.requiresInstance) { // Extract parameter from instance parameter - if (createInstance instanceof Resource && - createInstance._generatedId) { - + if (createInstance instanceof Resource && createInstance._generatedId) { options[createMethod.param] = createInstance._id(); } } else if (self._generatedId) { @@ -929,10 +933,7 @@ function attachOperations () { args.push(swaggerError); // Run operation against swagger - self._client._swagger.apis[self._resource][operation].apply( - null, - args - ); + self._client._swagger.apis[self._resource][operation].apply(null, args); /** * Handle error from Swagger. @@ -944,9 +945,12 @@ function attachOperations () { * @private * @param {Error} err - error object */ - function swaggerError (err) { + function swaggerError(err) { + var endTime = Date.now(); + var duration = endTime - startTime; + if (self.onError) self.onError(resource, operation, duration); if (err && err.data) { - err = new Error(err.data.toString('utf-8')); + err = new Error(err.data.toString("utf-8")); } reject(err); @@ -962,33 +966,22 @@ function attachOperations () { * @private * @param {Object} response - response from swagger */ - function processResponse (response) { - var result; - - if (respType === 'binary') { - result = Buffer.from(response.data); - } else { - result = response.data.toString('utf-8'); - if (respType !== null && result) { - result = JSON.parse(result); - } + function processResponse(response) { + var endTime = Date.now(); + var duration = endTime - startTime; + if (self.onResponse) self.onResponse(resource, operation, duration, response); + var result = response.data.toString("utf-8"); + if (respType !== null && result) { + result = JSON.parse(result); } - if (_.includes(knownTypes, respType) && - module.exports[respType] !== undefined) { - + if (_.includes(knownTypes, respType) && module.exports[respType] !== undefined) { if (multi) { result = _.map(result, function (obj) { - return module.exports[respType]( - self._client, - obj - ); + return module.exports[respType](self._client, obj); }); } else { - result = module.exports[respType]( - self._client, - result - ); + result = module.exports[respType](self._client, result); } } diff --git a/package.json b/package.json index 8364cb8..e6dda0c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ari-client", - "version": "2.2.0", + "version": "2.2.2", "description": "JavaScript client for Asterisk REST Interface.", "homepage": "https://github.com/asterisk/node-ari-client", "keywords": [