UNPKG

@ipsolution/ari-client

Version:

JavaScript client for Asterisk REST Interface. (fork IP Solution)

1,192 lines (1,080 loc) 31.8 kB
/** * First class object Resources found in ARI. Properties are attached to these * instances to provide instance specific callable operations. * * @module resources * * @copyright 2014, Digium, Inc. * @license Apache License, Version 2.0 * @author Samuel Fortier-Galarneau <sgalarneau@digium.com> */ 'use strict'; var util = require('util'); var _ = require('lodash'); var uuid = require('uuid'); var Promise = require('bluebird'); 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' ]; // Used to convert lists of resources to lists of objects var swaggerListTypeRegex = /List\[([A-Za-z]+)\]/; /** * Abstract ARI Resource. * * @class * @constructor * @param {Client} client - client instance * @param {string} id - resource identifier * @param {Object} objValues - ownProperties to be copied on the instance * @prop {Client} _client - ARI client instance * @prop {string} id - resource identifier */ function Resource (client, id, objValues) { var self = this; self._client = client; // id is optional if (!objValues) { objValues = id; } _.each(_.keys(objValues), function (key) { self[key] = objValues[key]; }); // if creation did not come from swagger, generate id if (!objValues) { self.generateId(); } // if second argument is a string, use it as an id if (_.isString(id)) { self._id(id); self._generatedId = true; } } /** * Generates a unique id for the resource. * * @method generateId * @memberof module:resources~Resource */ Resource.prototype.generateId = function () { var self = this; var id = uuid.v4(); self._id(id); self._generatedId = true; }; /** * Attaches an event to the client. * * @method on * @memberof module:resources~Resource * @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) { var self = this; var id = self._id() && self._id().toString(); var listeners = self._client._instanceListeners; // register event for this instance if (!listeners[event]) { listeners[event] = []; } listeners[event].push({once: false, id: id, callback: callback}); self._client.on(util.format('%s-%s', event, id), callback); }; /** * Attaches an event to the client to be emitted once (not yet implemented). * * @method once * @memberof module:resources~Resource * @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) { var self = this; var id = self._id() && self._id().toString(); var listeners = self._client._instanceListeners; // register event for this instance if (!listeners[event]) { listeners[event] = []; } listeners[event].push({once: true, id: id, callback: callback}); self._client.once(util.format('%s-%s', event, id), callback); }; /** * Alias for on. * * @method addListener * @memberof module:resources~Resource * @param {string} event - event name * @param {onceCallback} callback - callback invoked on event */ Resource.prototype.addListener = Resource.prototype.on; /** * Unattached the given callback for the given event from this resource. * * @method removeListener * @memberof module:resources~Resource * @param {string} event - event name * @param {Function} callback - the callback to unattach */ Resource.prototype.removeListener = function (event, callback) { var self = this; var id = self._id() && self._id().toString(); var listeners = self._client._instanceListeners; // unregister event for this instance if (listeners[event]) { var updatedListeners = _.filter(listeners[event], function(listener) { return listener.id !== id || listener.callback !== callback; }); var instanceListeners = _.filter(listeners[event], function(listener) { return listener.id === id && listener.callback === callback; }); // if multiple, remove the last listener registered if (instanceListeners.length) { Array.prototype.push.apply(updatedListeners, instanceListeners); updatedListeners.splice(-1); } self._client._instanceListeners[event] = updatedListeners; self._client.removeListener(util.format('%s-%s', event, id), callback); } }; /** * Unattached all callbacks for the given event from this resource. * * @method removeAllListeners * @memberof module:resources~Resource * @param {string} event - event name */ Resource.prototype.removeAllListeners = function (event) { var self = this; var id = self._id() && self._id().toString(); var listeners = self._client._instanceListeners; // unregister event for this instance if (listeners[event]) { 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)); } }; /** * Application object for application API responses. * * @class Application * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function Application (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(Application, Resource); /** * Get or set id for the application. * * @method _id * @memberof module:resources~Application * @param {string} value - value to assign to the id property */ Application.prototype._id = function (value) { var self = this; if (value) { self.name = value; } else { return self.name; } }; /** * The name of the identifier field used when passing as parameter. * * @member {string} _param * @memberof module:resources~Application */ Application.prototype._param = 'applicationName'; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Application */ Application.prototype._resource = 'applications'; /** * Asterisk object for asterisk API responses. * * @class Asterisk * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {Object} objValues - ownProperties to copy to the instance */ function Asterisk (client, objValues) { var self = this; Resource.call(self, client, undefined, objValues); } util.inherits(Asterisk, Resource); /** * The name of the identifier field used when passing as parameter. * * @member {string} _param * @memberof module:resources~Asterisk */ Asterisk.prototype._param = ''; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Asterisk */ Asterisk.prototype._resource = 'asterisk'; /** * Bridge object for bridge API responses. * * @class Bridge * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function Bridge (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(Bridge, Resource); /** * Get or set id for the bridge. * * @method _id * @memberof module:resources~Bridge * @param {string} value - value to assign to the id property */ Bridge.prototype._id = function (value) { var self = this; if (value) { self.id = value; } else { return self.id; } }; /** * The name of the identifier field used when passing as parameter. * * @member {string} _param * @memberof module:resources~Bridge */ Bridge.prototype._param = 'bridgeId'; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Bridge */ Bridge.prototype._resource = 'bridges'; /** * Used to pass generated ids into create methods. * * @member {Object} _createMethods * @memberof module:resources~Bridge * @prop {Object} create - create method config * @prop {string} create.param - parameter name to be passed in * @prop {Object} play - play method config * @prop {string} play.param - parameter name to be passed in * @prop {boolean} play.requiresInstance - whether instance is required to be * passed in * @prop {Object} record - record method config * @prop {string} record.param - parameter name to be passed in * @prop {boolean} record.requiresInstance - whether instance is required to be * passed in */ Bridge.prototype._createMethods = { create: { param: Bridge.prototype._param }, // this is not currently supported in ARI play: { param: 'playbackId', requiresInstance: true }, record: { param: 'name', requiresInstance: true } }; /** * Channel object for channel API responses. * * @class Channel * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function Channel (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(Channel, Resource); /** * Get or set id for the channel. * * @method _id * @memberof module:resources~Channel * @param {string} value - value to assign to the id property */ Channel.prototype._id = function (value) { var self = this; if (value) { self.id = value; } else { return self.id; } }; /** * The name of the identifier field used when passing as parameter. * * @member {string} _param * @memberof module:resources~Channel */ Channel.prototype._param = 'channelId'; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Channel */ Channel.prototype._resource = 'channels'; /** * Used to pass generated ids into create methods. * * @member {Object} _createMethods * @memberof module:resources~Channel * @prop {Object} create - create method config * @prop {string} create.param - parameter name to be passed in * @prop {Object} originate - originate method config * @prop {string} originate.param - parameter name to be passed in * @prop {Object} snoopChannel - snoopChannel method config * @prop {string} snoopChannel.param - parameter name to be passed in * @prop {boolean} snoopChannel.requiresInstance - whether instance is required * to be passed in * @prop {Object} play - play method config * @prop {string} play.param - parameter name to be passed in * @prop {boolean} play.requiresInstance - whether instance is required to be * passed in * @prop {Object} record - record method config * @prop {string} record.param - parameter name to be passed in * @prop {boolean} record.requiresInstance - whether instance is required to be * passed in * @prop {Object} externalMedia - create method config * @prop {string} externalMedia.param - parameter name to be passed in */ Channel.prototype._createMethods = { create: { param: Channel.prototype._param }, originate: { param: Channel.prototype._param }, snoopChannel: { param: 'snoopId', requiresInstance: true }, play: { param: 'playbackId', requiresInstance: true }, record: { param: 'name', requiresInstance: true }, externalMedia: { param: Channel.prototype._param } }; /** * DeviceState object for deviceState API responses. * * @class DeviceState * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function DeviceState (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(DeviceState, Resource); /** * Get or set id for the deviceState. * * @method _id * @memberof module:resources~DeviceState * @param {string} value - value to assign to the id property */ DeviceState.prototype._id = function (value) { var self = this; if (value) { self.name = value; } else { return self.name; } }; /** * The name of the identifier field used when passing as parameter. * * @member {string} _param * @memberof module:resources~DeviceState */ DeviceState.prototype._param = 'deviceName'; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~DeviceState */ DeviceState.prototype._resource = 'deviceStates'; /** * Endpoint object for endpoint API responses. * * @class Endpoint * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function Endpoint (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(Endpoint, Resource); /** * Get or set id for the endpoint. * * @method _id * @memberof module:resources~Endpoint * @param {string} value - value to assign to the id property */ Endpoint.prototype._id = function (value) { var self = this; // multi field id if (value) { self.technology = value.technology; self.resource = value.resource; } else { return { tech: self.technology, resource: self.resource, toString: function () { return util.format('%s/%s', self.technology, self.resource); } }; } }; /** * The name of the identifier field used when passing as parameter. * * @member {string} _param * @memberof module:resources~Endpoint */ Endpoint.prototype._param = ['tech', 'resource']; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Endpoint */ Endpoint.prototype._resource = 'endpoints'; /** * LiveRecording object for liveRecording API responses. * * @class LiveRecording * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function LiveRecording (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(LiveRecording, Resource); /** * Get or set id for the liveRecording. * * @method _id * @memberof module:resources~LiveRecording * @param {string} value - value to assign to the id property */ LiveRecording.prototype._id = function (value) { var self = this; if (value) { self.name = value; } else { return self.name; } }; /** * The name of the identifier field used when passing as parameter. * * @member {string} _param * @memberof module:resources~LiveRecording */ LiveRecording.prototype._param = 'recordingName'; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~LiveRecording */ LiveRecording.prototype._resource = 'recordings'; /** * Mailbox object for mailbox API responses. * * @class Mailbox * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function Mailbox (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(Mailbox, Resource); /** * Get or set id for the mailbox. * * @method _id * @memberof module:resources~Mailbox * @param {string} value - value to assign to the id property */ Mailbox.prototype._id = function (value) { var self = this; if (value) { self.name = value; } else { return self.name; } }; /** * The name of the identifier field used when passing as parameter.. * * @member {string} _param * @memberof module:resources~Mailbox */ Mailbox.prototype._param = 'mailboxName'; /** * The name of this resource. . * * @member {string} _resource * @memberof module:resources~Mailbox */ Mailbox.prototype._resource = 'mailboxes'; /** * Playback object for playback API responses. * * @class Playback * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function Playback (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(Playback, Resource); /** * Get or set id for the playback. * * @method _id * @memberof module:resources~Playback * @param {string} value - value to assign to the id property */ Playback.prototype._id = function (value) { var self = this; if (value) { self.id = value; } else { return self.id; } }; /** * The name of the identifier field used when passing as parameter.. * * @member {string} _param * @memberof module:resources~Playback */ Playback.prototype._param = 'playbackId'; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Playback */ Playback.prototype._resource = 'playbacks'; /** * Sound object for sound API responses. * * @class Sound * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function Sound (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(Sound, Resource); /** * Get or set id for the sound. * * @method _id * @memberof module:resources~Sound * @param {string} value - value to assign to the id property */ Sound.prototype._id = function (value) { var self = this; if (value) { self.id = value; } else { return self.id; } }; /** * The name of the identifier field used when passing as parameter. * * @member {string} _param * @memberof module:resources~Sound */ Sound.prototype._param = 'soundId'; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~Sound */ Sound.prototype._resource = 'sounds'; /** * StoredRecording object for storedRecording API responses. * * @class StoredRecording * @constructor * @extends Resource * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to the instance */ function StoredRecording (client, id, objValues) { var self = this; Resource.call(self, client, id, objValues); } util.inherits(StoredRecording, Resource); /** * Get or set id for the storedRecording. * * @method _id * @memberof module:resources~StoredRecording * @param {string} value - value to assign to the id property */ StoredRecording.prototype._id = function (value) { var self = this; if (value) { self.name = value; } else { return self.name; } }; /** * The name of the identifier field used when passing as parameter. * * @member {string} _param * @memberof module:resources~StoredRecording */ StoredRecording.prototype._param = 'recordingName'; /** * The name of this resource. * * @member {string} _resource * @memberof module:resources~StoredRecording */ StoredRecording.prototype._resource = 'recordings'; /** * Attach operations to the resource instance bound to this. * * @method attachOperations * @memberof module:resources * @this module:resources~Resource * @private */ function attachOperations () { /*jshint validthis:true*/ var self = this; var operations = self._client._swagger.apis[self._resource].operations; _.each(_.keys(operations), attachOperation); /** * Attach operation to resource instance. * * @callback attachOperationsCallback * @memberof module:resources~attachOperations * @method attachOperation * @private * @param {string} operation - the operation to attach */ function attachOperation (operation) { self[operation] = callSwagger; var oper = self._client._swagger.apis[self._resource].operations[operation]; var respType = oper.type; var multi = false; var regexArr = swaggerListTypeRegex.exec(respType); if (regexArr !== null) { respType = regexArr[1]; multi = true; } var params = oper.parameters; /** * Responsible for calling API through Swagger. * * @callback attachOperationCallback * @memberof module:resources~attachOperations~attachOperation * @method callSwagger * @private * @param {Object} parameters - parameters to swagger * @param {Resource} [createInstance] - instance to get identifier from for * purposes of creating the resource through ARI * @param {Function} callback - callback invoked with swagger response */ function callSwagger (/* args..., callback */) { // Separate user callback from other args and inject object id(s) // if appropriate var args = _.toArray(arguments); var options = _.first(args); var createInstance = args[1]; var userCallback = (_.isFunction(_.last(args))) ? _.last(args) : undefined; return new Promise(function(resolve, reject) { args = []; if (options === undefined || options === null || _.isFunction(options) || _.isArray(options)) { options = {}; } else { // Swagger can alter options passed in options = _.clone(options); // convert body params for swagger options = _utils.parseBodyParams(params, options); } _.each(params, function (param) { var expectedParam = param.name; var actualParam = self._param; if (_.isArray(actualParam)) { actualParam = _.find(actualParam, function (candidate) { return candidate === expectedParam; }); } // Inject parameter using instance value if (expectedParam === actualParam && param.required) { var identifier = self._id() || ''; // In case of multi ids options[expectedParam] = identifier[expectedParam] || identifier; } }); // Inject create method parameters if instance has client // generated id. if (_.includes(_.keys(self._createMethods), operation)) { var createMethod = self._createMethods[operation]; if (createMethod.requiresInstance) { // Extract parameter from instance parameter if (createInstance instanceof Resource && createInstance._generatedId) { options[createMethod.param] = createInstance._id(); } } else if (self._generatedId) { options[createMethod.param] = self._id(); } } args.push(options); // Inject response processing callback args.push(processResponse); // Inject error handling callback args.push(swaggerError); // Run operation against swagger self._client._swagger.apis[self._resource][operation].apply( null, args ); /** * Handle error from Swagger. * * @callback callSwaggerErrorCallback * @method swaggerError * @memberof * module:resources~attachOperations~attachOperation~callSwagger * @private * @param {Error} err - error object */ function swaggerError (err) { if (err && err.data) { err = new Error(err.data.toString('utf-8')); } reject(err); } /** * Process response from Swagger. * * @callback callSwaggerSuccessCallback * @method processResponse * @memberof * module:resources~attachOperations~attachOperation~callSwagger * @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); } } if (_.includes(knownTypes, respType) && module.exports[respType] !== undefined) { if (multi) { result = _.map(result, function (obj) { return module.exports[respType]( self._client, obj ); }); } else { result = module.exports[respType]( self._client, result ); } } resolve(result); } }).asCallback(userCallback); } } } /** * The known resource types. * * @member {string[]} knownTypes * @memberof module:resources */ module.exports.knownTypes = knownTypes; /** * Regex used to determine if return type is a list of a known resource type. * * @member {Regex} swaggerListTypeRegex * @memberof module:resources */ module.exports.swaggerListTypeRegex = swaggerListTypeRegex; /** * Creates a new Application instance. * * @memberof module:resources * @method Application * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~Application} application */ module.exports.Application = function (client, id, objValues) { var application = new Application(client, id, objValues); attachOperations.call(application); return application; }; /** * Creates a new Asterisk resource. * * @memberof module:resources * @method Asterisk * @param {Client} client - ARI client instance * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~Asterisk} asterisk resource */ module.exports.Asterisk = function (client, objValues) { var asterisk = new Asterisk(client, objValues); attachOperations.call(asterisk); return asterisk; }; /** * Creates a new Bridge instance. * * @memberof module:resources * @method Bridge * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~Bridge} bridge */ module.exports.Bridge = function (client, id, objValues) { var bridge = new Bridge(client, id, objValues); attachOperations.call(bridge); return bridge; }; /** * Creates a new Channel instance. * * @memberof module:resources * @method Channel * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~Channel} channel */ module.exports.Channel = function (client, id, objValues) { var channel = new Channel(client, id, objValues); attachOperations.call(channel); return channel; }; /** * Creates a new DeviceState instance. * * @memberof module:resources * @method DeviceState * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~DeviceState} deviceState */ module.exports.DeviceState = function (client, id, objValues) { var deviceState = new DeviceState(client, id, objValues); attachOperations.call(deviceState); return deviceState; }; /** * Creates a new Endpoint instance. * * @memberof module:resources * @method Endpoint * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~Endpoint} endpoint */ module.exports.Endpoint = function (client, id, objValues) { var endpoint = new Endpoint(client, id, objValues); attachOperations.call(endpoint); return endpoint; }; /** * Creates a new LiveRecording instance. * * @memberof module:resources * @method LiveRecording * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~LiveRecording} liveRecording */ module.exports.LiveRecording = function (client, id, objValues) { var liveRecording = new LiveRecording(client, id, objValues); attachOperations.call(liveRecording); return liveRecording; }; /** * Creates a new Mailbox instance. * * @memberof module:resources * @method Mailbox * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~Mailbox} mailbox */ module.exports.Mailbox = function (client, id, objValues) { var mailbox = new Mailbox(client, id, objValues); attachOperations.call(mailbox); return mailbox; }; /** * Creates a new Playback instance. * * @memberof module:resources * @method Playback * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~Playback} playback */ module.exports.Playback = function (client, id, objValues) { var playback = new Playback(client, id, objValues); attachOperations.call(playback); return playback; }; /** * Creates a new Sound instance. * * @memberof module:resources * @method Sound * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~Sound} sound */ module.exports.Sound = function (client, id, objValues) { var sound = new Sound(client, id, objValues); attachOperations.call(sound); return sound; }; /** * Creates a new StoredRecording instance. * * @memberof module:resources * @method StoredRecording * @param {Client} client - ARI client instance * @param {string} id - Application identifier * @param {Object} objValues - ownProperties to copy to instance * @returns {module:resources~StoredRecording} storedRecording */ module.exports.StoredRecording = function (client, id, objValues) { var storedRecording = new StoredRecording(client, id, objValues); attachOperations.call(storedRecording); return storedRecording; };