'use strict'; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /* * Nexmo Client SDK * Main wrapper * * Copyright (c) Nexmo Inc. */ const WildEmitter = require('wildemitter'); const loglevel_plugin_prefix_1 = __importDefault(require("loglevel-plugin-prefix")); const loglevel_1 = __importDefault(require("loglevel")); const nexmoClientError_1 = require("./nexmoClientError"); const socket_io_client_1 = __importDefault(require("socket.io-client")); const js_1 = __importDefault(require("@bugsnag/js")); const publicip_1 = __importDefault(require("./modules/publicip")); const utils_1 = __importDefault(require("./utils")); const application_1 = __importDefault(require("./application")); const errors_emitter_1 = __importDefault(require("./modules/errors_emitter")); const user_1 = __importDefault(require("./user")); const rtc_helper_1 = __importDefault(require("./modules/rtc_helper")); loglevel_plugin_prefix_1.default.reg(loglevel_1.default); loglevel_plugin_prefix_1.default.apply(loglevel_1.default, { template: '[%t] %l (NXM-%n):', timestampFormatter: (date) => { return date.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1'); }, levelFormatter: (level) => { return level.toUpperCase(); }, nameFormatter: (name) => { return name || 'SDK'; } }); /** * The parent NexmoClient class. * * @class NexmoClient * * @param {object} params the settings to initialise the SDK * @param {string} params.debug='silent' set mode to 'debug', 'info', 'warn', or 'error' for customized logging levels in the console * @param {string} params.url='nexmo_ws_url' Nexmo Conversation Websocket url, default is wss://ws.nexmo.com (wss://ws-us-1.nexmo.com for WDC, wss://ws-us-2.nexmo.com for DAL, wss://ws-eu-1.nexmo.com for LON, wss://ws-sg-1.nexmo.com for SNG) * @param {string} params.nexmo_api_url=Nexmo Conversation Api url, default is https://api.nexmo.com (https://api-us-1.nexmo.com for WDC, https://api-us-2.nexmo.com for DAL, https://api-eu-1.nexmo.com for LON, https://api-sg-1.nexmo.com for SNG) * @param {string} params.ips_url='ips_url' Nexmo IPS url for image upload, default is https://api.nexmo.com/v1/image (https://api-us-1.nexmo.com/v1/image for WDC, https://api-us-2.nexmo.com/v1/image for DAL, https://api-eu-1.nexmo.com/v1/image for LON, https://api-sg-1.nexmo.com/v1/image for SNG) * @param {string} params.path='/v2/rtc' Nexmo Conversation Websocket url path suffix * @param {RTCStatsConfig} params.rtcstats set reporting for stream statistics (Internal event emit) * @param {Boolean} params.rtcstats.emit_events=false receive rtcstats:report event (deprecated) * @param {Boolean} params.rtcstats.emit_rtc_analytics=false receive rtcstats:analytics event * @param {number} params.rtcstats.emit_interval=1000 interval in ms for rtcstats:report and rtcstats:analytics * @param {Boolean} params.rtcstats.remote_collection=true collect client logs internally * @param {Boolean} params.rtcstats.remote_collection_url='gollum_url' url for collecting client logs internally * @param {number} params.rtcstats.remote_collection_interval=5000 interval in ms to collect client logs internally * @param {object} params.socket_io configure socket.io * @param {Boolean} params.socket_io.forceNew=true configure socket.io forceNew attribute * @param {Boolean} params.socket_io.autoConnect=true socket.io autoConnect attribute * @param {Boolean} params.socket_io.reconnection=true socket.io reconnection attribute * @param {number} params.socket_io.reconnectionAttempts=5 socket.io reconnectionAttempts attribute * @param {string[]} params.socket_io.transports='websocket' socket.io transports protocols * @param {string} params.sync='none' {'none' || 'lite' || 'full'} after a successful session creation, synchronise conversations, include events or nothing * @param {string} params.environment='production' development / production environment * @param {object[]} [params.iceServers=[]] configure iceServers for RTCPeerConnection * @param {object} params.log_reporter configure log reports for bugsnag tool * @param {Boolean} params.log_reporter.enabled=true * @param {string} params.log_reporter.bugsnag_key your bugsnag api key / defaults to Nexmo api key * @param {object} params.conversations_page_config configure paginated requests for conversations * @param {number} params.conversations_page_config.page_size=10 the number of resources returned in a single request list * @param {string} params.conversations_page_config.order=asc 'asc' or 'desc' ordering of resources (usually based on creation time) * @param {string} params.conversations_page_config.cursor cursor parameter to access the next or previous page of a data set * @param {object} params.events_page_config configure paginated requests for events * @param {number} params.events_page_config.page_size=10 the number of resources returned in a single request list * @param {string} params.events_page_config.order=asc 'asc' or 'desc' ordering of resources (usually based on creation time) * @param {string} params.events_page_config.event_type the type of event used to filter event requests. Supports wildcard options with :* eg. 'members:*' * @param {Boolean} params.enableEventsQueue=true mechanism to guarantee order of events received during a session * @param {Boolean} params.enableInboundOffer=false configure receiving offer for faster inbound call setup * @param {Boolean} params.acknowledgeEvents=true send acknowledgment over websocket to server for received events * @param {string} params.token the jwt token for network requests * * @emits NexmoClient#disconnect * @emits NexmoClient#error * @emits NexmoClient#ready * @emits NexmoClient#reconnect * @emits NexmoClient#reconnecting */ class NexmoClient { constructor(params = {}) { // save an array of instances const inputParams = params; this.config = { debug: 'silent', log_reporter: { enabled: false, bugsnag_key: null }, environment: '@@environment', ips_url: '@@ips_url', nexmo_api_url: '@@nexmo_api_url', path: '/v2/rtc', repository: '@@repository', SDK_version: '@@SDK_version', socket_io: { reconnection: true, reconnectionAttempts: 5, reconnectionDelay: 2000, randomizationFactor: 0.75, reconnectionDelayMax: 15000, forceNew: true, autoConnect: true, transports: ['websocket'], }, sync: 'none', url: '@@ws_url', iceServers: [], rtcstats: { remote_collection: true, remote_collection_url: '@@gollum_url', remote_collection_interval: 5000, emit_events: false, emit_rtc_analytics: false, emit_interval: 1000, }, conversations_page_config: { page_size: 10, order: 'asc', cursor: '' }, events_page_config: { page_size: 10, order: 'asc', event_type: '' }, enableEventsQueue: true, enableInboundOffer: false, acknowledgeEvents: true, token: null }; this.config.socket_io.query = { token: '', SDK_version: this.config.SDK_version, session_version: params.enableInboundOffer ? '0.0.3' : '0.0.1', acknowledge_events: this.config.acknowledgeEvents, OS_family: 'js', OS_revision: (typeof navigator !== 'undefined') ? navigator.userAgent : (typeof window !== 'undefined') ? window.navigator.userAgent : 'Generic JS navigator' }; this.sessionReady = false; this.disconnectTimestamp = null; this.session_id = null; this.apiKey = null; this.requests = {}; this.application = null; /* Definitions of log levels error: major error messages, some lost functionality warn: error messages which do not cause a functional failure info: informational messages, showing completion, progress, etc. debug: messages to help in diagnosing a problem */ if (['debug', 'info', 'warn', 'error'].includes(inputParams.debug)) { loglevel_1.default.setLevel(inputParams.debug); } else if (inputParams.debug === true) { loglevel_1.default.setLevel('debug'); } else { loglevel_1.default.setLevel('silent'); } this.log = loglevel_1.default.noConflict(); // set our config from the inputParams this.config = utils_1.default.deepMergeObj(this.config, this._sanitizeConfig(inputParams)); // inject bug reporting tool if (this.config.log_reporter.enabled) { const bugsnagConfig = { apiKey: this.config.log_reporter.bugsnag_key || utils_1.default._getBugsnagKey(), appVersion: this.config.socket_io.query.SDK_version, releaseStage: this.config.environment }; global.NXMbugsnagClient = js_1.default(bugsnagConfig); } WildEmitter.mixin(NexmoClient); } /** * Creates and sets the socket_io connection * * @private */ _createAndSetConnection() { let connection; // Create the socket.io connection and allow multiple instances let socket_io_config = Object.assign({ path: this.config.path }, this.config.socket_io); connection = socket_io_client_1.default.connect(this.config.url, socket_io_config); this.connection = connection; // Listen to socket.io events /** * Client listening for ready event. * * @event NexmoClient#ready * @example <caption>Listen for websocket ready event </caption> * rtc.on("ready", () => { * console.log("connection ready"); * }); */ connection.on('connect', () => { this.emit('ready'); this.sessionReady = true; this.log.info('websocket ready'); }); /** * Client listening for disconnect event. * * @event NexmoClient#disconnect * @example <caption>Listen for websocket disconnect event </caption> * rtc.on("disconnect", (reason) => { * console.log("disconnect", reason); * }); */ connection.on('disconnect', (reason, details) => { this.emit('disconnect', (reason === "io client disconnect") ? NexmoClient.DISCONNECT_REASON.ClientDisconnected : (reason === "io server disconnect" && this.session_id) ? NexmoClient.DISCONNECT_REASON.TokenExpired : NexmoClient.DISCONNECT_REASON.ConnectionError); this.disconnectTimestamp = Date.now(); this.log.info('websocket disconnected', reason, details); }); /** * Client listening for reconnect event. * * @event NexmoClient#reconnect * @example <caption>Listen for websocket reconnect event </caption> * rtc.on("reconnect", (retry_number) => { * console.log("reconnect", retry_number); * }); */ connection.io.on('reconnect', (retry_number) => { this.emit('reconnect', retry_number); this.log.info('websocket reconnect', retry_number); utils_1.default.networkRequest({ url: `${this.config.url.replace('wss://', 'https://')}/v2/rtc/metrics`, type: 'POST', token: this.config.token, data: { sessionId: this.session_id, metrics: [ { name: 'TIME_TO_RECONNECT_SUCCESS', time: this.disconnectTimestamp ? Date.now() - this.disconnectTimestamp : undefined, attempt: retry_number } ] } }).catch((err) => { this.log.warn('Failed to publish socket reconnection metrics', err); }); }); /** * Client listening for reconnecting event. * * @event NexmoClient#reconnecting * @example <caption>Listen for websocket reconnecting event </caption> * rtc.on("reconnecting", (retry_number): void => { * console.log("reconnecting", retry_number); * }); */ connection.io.on('reconnect_attempt', (retry_number) => { this.emit('reconnecting', retry_number); this.log.info('websocket reconnecting', retry_number); }); /** * Client listening for error event. * * @event NexmoClient#error * @example <caption>Listen for websocket error event </caption> * rtc.on("error", (error) => { * console.log("error", error); * }); */ connection.on('connect_error', (error) => { this.emit('error', new nexmoClientError_1.NexmoClientError(error)); this.log.error('Socket.io reported a generic error', error); }); connection.io.on("reconnect_failed", () => { this.emit('error', new nexmoClientError_1.NexmoClientError("error:client:reconnection_failed")); this.log.error('websocket reconnection error'); }); connection.io.on('packet', (packet) => { if (packet.type !== 2) return; if (packet.data[0] === 'echo') return; // ignore echo events const callbackAck = packet.data[2]; if (callbackAck && typeof (callbackAck) === "function") { callbackAck(); } const response = packet.data[1]; // Set the type of the response response.type = packet.data[0]; this.log.debug('<--', response.type, response); if (this.requests['session:login']) { const callback = this.requests['session:login'].callback; delete this.requests['session:login']; callback(response); } else if (response.rid in this.requests) { const callback = this.requests[response.rid].callback; delete this.requests[response.rid]; delete response.delay; if (this.errorsEmitter) { this.errorsEmitter.emitResponseIfError(response); } callback(response); } else { // This is an unsolicited event we emit it in application level // Excluding session:* events from being processed and check if event type is a system:error:* one if (this.errorsEmitter && response.type.startsWith('system:error:')) { this.errorsEmitter.emitResponseIfError(response); } else if (response.type.startsWith('session:')) { // Handle Events emitted from Reconnection this.updateSession(response); } else if (this.application) { this.application._enqueueEvent(response); } } }); return connection; } /** * Revert any invalid params to our default * * @param {object} config the object to sanitize * @private */ _sanitizeConfig(incomingConfig) { // make sure we allow specific values for the params // Sync let sanitizedConfig = incomingConfig; if (incomingConfig.sync && ['none', 'lite', 'full'].indexOf(incomingConfig.sync) === -1) { this.log.warn(`invalid param '${incomingConfig.sync}' for sync, reverting to ${this.config.sync}`); sanitizedConfig.sync = this.config.sync; } return sanitizedConfig; } /** * Conversation listening for text events. * * @event Conversation#text * * @property {Member} sender - The sender of the text * @property {TextEvent} text - The text message received * @example <caption>listen for text events</caption> * conversation.on("text",(sender, message) => { * console.log(sender, message); * // Identify your own message. * if (message.from === conversation.me.id){ * renderMyMessages(message) * } else { * renderOtherMessages(message) * } * }); */ /** * * Conversation listening for image events. * * @event Conversation#image * * @property {Member} sender - The sender of the image * @property {ImageEvent} image - The image message received * @example <caption>listen for image events</caption> * conversation.on("image", (sender, image) => { * console.log(sender,image); * // Identify if your own imageEvent or someone else's. * if (image.from !== conversation.me.id){ * displayImages(image); * } * }); */ /** * Conversation listening for deleted events. * * @event Conversation#event:delete * * @property {Member} member - the Member who deleted an event * @property {NXMEvent} event - deleted event: event.id * @example <caption>get details about the deleted event</caption> * conversation.on("event:delete", (member, event) => { * console.log(event.id); * console.log(event.body.timestamp.deleted); * }); */ /** * Conversation listening for ephemeral events. * @param {object} body customizable key value pairs * * @returns {Promise<NXMEvent>} - the ephemeral event that was sent * * @example <caption> get ephemeral event </caption> * conversation.on("ephemeral", (member, event) => { * console.log(event); * }); * */ /** * Conversation listening for new Members. * * @event Conversation#member:joined * * @property {Member} member - the Member that joined * @property {NXMEvent} event - the join event * @example <caption>get the name of the new Member</caption> * conversation.on("member:joined", (member, event) => { * console.log(event.id) * console.log(member.userName+ " joined the conversation"); * }); */ /** * Conversation listening for Members being invited. * * @event Conversation#member:invited * * @property {Member} member - the Member that is invited * @property {NXMEvent} event - data regarding the receiver of the invitation * @example <caption>get the name of the invited Member</caption> * conversation.on("member:invited", (member, event) => { * console.log(member.userName + " invited to the conversation"); * }); */ /** * Conversation listening for Members callStatus changes. * * @event Conversation#member:call:status * * @property {Member} member - the Member that has left * @example <caption>get the callStatus of the member that changed call status</caption> * conversation.on("member:call:status", (member) => { * console.log(member.callStatus); * }); */ /** * Conversation listening for Members leaving (kicked or left). * * @event Conversation#member:left * * @property {Member} member - the Member that has left * @property {NXMEvent} event - data regarding the receiver of the invitation * @example <caption>get the username of the Member that left</caption> * conversation.on("member:left", (member , event) => { * console.log(member.userName + " left"); * console.log(event.body.reason); * }); */ /** * Conversation listening for Members typing. * * @event Conversation#text:typing:on * * @property {Member} member - the member that started typing * @property {NXMEvent} event - the start typing event * @example <caption>get the display name of the Member that is typing</caption> * conversation.on("text:typing:on", (member, event) => { * console.log(member.displayName + " is typing..."); * }); */ /** * Conversation listening for Members stopped typing. * * @event Conversation#text:typing:off * * @property {Member} member - the member that stopped typing * @property {NXMEvent} event - the stop typing event * @example <caption>get the display name of the Member that stopped typing</caption> * conversation.on("text:typing:off", (member, event) => { * console.log(member.displayName + " stopped typing..."); * }); */ /** * Conversation listening for Members' seen texts. * * @event Conversation#text:seen * * @property {Member} member - the Member that saw the text * @property {TextEvent} text - the text that was seen * @example <caption>listen for seen text events</caption> * conversation.on("text:seen", (member, text) => { * console.log(text); * if (conversation.me.id !== member.memberId) { * console.log(member); * } * }); */ /** * Conversation listening for Members' seen images. * @event Conversation#image:seen * * @property {Member} member - the member that saw the image * @property {ImageEvent} image - the image that was seen * @example <caption>listen for seen image events</caption> * conversation.on("image:seen", (member, image) => { * console.log(image); * if (conversation.me.id !== member.memberId) { * console.log(member); * }; * }); */ /** * Conversation listening for Members submitted messages. * @event Conversation#message:submitted * * @property {Member} member - the member that message was submitted to * @property {MessageEvent} message - the message that was submitted * @example <caption>listen for submitted message events</caption> * conversation.on("message:submitted", (member, message) => { * console.log(message); * if (conversation.me.id !== member.memberId) { * console.log(member); * }; * }); */ /** * Conversation listening for Members rejected messages. * @event Conversation#message:rejected * * @property {Member} member - the member that message was rejected by * @property {MessageEvent} message - the message that was rejected * @example <caption>listen for rejected message events</caption> * conversation.on("message:rejected", (member, message) => { * console.log(message); * if (conversation.me.id !== member.memberId) { * console.log(member); * }; * }); */ /** * Conversation listening for Members undeliverable messages. * @event Conversation#message:undeliverable * * @property {Member} member - the member that message was undeliverable to * @property {MessageEvent} message - the message that was undeliverable * @example <caption>listen for undeliverable message events</caption> * conversation.on("message:undeliverable", (member, message) => { * console.log(message); * if (conversation.me.id !== member.memberId) { * console.log(member); * }; * }); */ /** * Conversation listening for Members delivered messages. * @event Conversation#message:delivered * * @property {Member} member - the member that message was delivered to * @property {MessageEvent} message - the message that was delivered * @example <caption>listen for delivered message events</caption> * conversation.on("message:delivered", (member, message) => { * console.log(message); * if (conversation.me.id !== member.memberId) { * console.log(member); * }; * }); */ /** * Conversation listening for Members seen messages. * @event Conversation#message:seen * * @property {Member} member - the member that message was seen by * @property {MessageEvent} message - the message that was seen * @example <caption>listen for seen message events</caption> * conversation.on("message:seen", (member, message) => { * console.log(message); * if (conversation.me.id !== member.memberId) { * console.log(member); * }; * }); */ /** * Conversation listening for Members media changes (audio,text) * * Change in media presence state. They are in the Conversation with text or audio. * * @event Conversation#member:media * * @property {Member} member - the Member object linked to this event * @property {NXMEvent} event - information about media presence state * @property {boolean} event.body.audio - is audio enabled * @example <caption>get every Member's media change events </caption> * conversation.on("member:media", (member, event) => { * console.log(event.body.media); //{"audio": true, "audio_settings": {"enabled": true, "muted": false, "earmuffed": false}} * }); */ /** * Conversation listening for mute on events * A Member has muted their audio * * @event Conversation#audio:mute:on * * @property {Member} member - the Member object linked to this event * @property {NXMEvent} event - information about the mute event * @example <caption>listen for audio mute on events </caption> * conversation.on("audio:mute:on", (member, event) => { * console.log("member that is muted ", member); * console.log(event); * }); */ /** * Conversation listening for mute off events * A member has unmuted their audio * * @event Conversation#audio:mute:off * * @property {Member} member - the member object linked to this event * @property {NXMEvent} event - information about the mute event * @example <caption>listen for audio mute off events </caption> * conversation.on("audio:mute:off", (member, event) => { * console.log("member that is unmuted ", member); * console.log(event); * }); */ sendRequest(request, callback) { // Add a message ID to the request and set up a listener for the reply (or error) request.tid = utils_1.default.allocateUUID(); const type = request.type; delete request.type; this.log.debug('-->', type, request); this.log.info('-->', type, request.tid); this.connection.emit(type, request); this.requests[request.tid] = { type: type, request, callback }; } async sendNetworkRequest(params) { const version = params.version || 'beta'; const url = `${this.config.nexmo_api_url}/${version}/${params.path}`; if (!(params.type === 'GET' || params.type === 'DELETE')) { if (params.data) { params.data.originating_session = this.session_id; } else { params.data = { originating_session: this.session_id }; } } try { const request = { type: params.type, url, data: (params.data) ? params.data : null, token: (params.data || {}).token ? params.data.token : this.config.token || null }; this.log.debug('sendNetworkRequest: ', { request }); const { response } = await utils_1.default.networkRequest(request); return response; } catch ({ response }) { throw response; } } /** * Create a new Session. * @param {string} token - the user JSON Web Token (JWT) * @returns {Promise<Application>} - the application logged in to * @example <caption>Create a session and return the Application</caption> * rtc.createSession(token).then((application) => { * console.log(application); * }).catch((error) => { * console.log(error); * }); */ createSession(token) { this.config.socket_io.query.token = token; this._createAndSetConnection(); // return a promise for the application return new Promise((resolve, reject) => { this.log.info(`Client-SDK Version: ${this.config.SDK_version}`); this.config.token = null; this.requests['session:login'] = { type: 'session:login', callback: async (response) => { if (response.type === 'session:success') { this.session_id = response.body.id; this.apiKey = response.body.api_key; // Store token in config this.config.token = token; // adds the session id as a query parameter in order to // connect to the same session in case of a reconnection this.connection.io.opts.query = { session_id: this.session_id, token: this.config.token }; if (!this.application || (this.application.me && this.application.me.id !== response.body.user_id)) { this.application = new application_1.default(this, {}); } if (!this.application.me) { this.application.me = new user_1.default(this.application, { id: response.body.user_id, name: response.body.name }); } if (!this.errorsEmitter) { this.errorsEmitter = new errors_emitter_1.default(this.application); } // Set Bugsnag user to application.me.id if (this.config.log_reporter.enabled) { global.NXMbugsnagClient.user = { id: this.application.me.id, name: this.application.me.name, session_id: response.body.id }; } if (this.config.sync !== 'none') { // Retrieve the existing conversation data for this user try { await this.application.getConversations(); resolve(this.application); } catch (error) { reject(error); } } else { resolve(this.application); } } else { reject(new nexmoClientError_1.NexmoApiError(response)); } } }; }); } /** * Delete existing Session. * @returns {Promise<CAPIResponse>} - response with rid and type * @example <caption>Delete existing session</caption> * rtc.deleteSession().then((response) => { * console.log(response); * }).catch((error) => { * console.log(error); * }); */ deleteSession() { return new Promise(async (resolve, reject) => { const logoutRequest = () => { return this.sendRequest({ type: 'session:logout', body: {} }, (response) => { if (response.type === 'session:logged-out' || response.type === 'session:terminated') { this.disconnect(); delete this.errorsEmitter; delete this.application; delete this.connection; this.requests = {}; this.sessionReady = false; resolve(response); } else { reject(response); } }); }; // prepare for deleteSession if (this.application) { let disablePromises = []; if (this.application.conversations.size) { for (let conversation of this.application.conversations.values()) { disablePromises.push(conversation.media.disable()); } } try { await Promise.all(disablePromises); } catch (error) { this.log.error("deleteSession: ", error); } return logoutRequest(); } else { return logoutRequest(); } }); } updateSession(event) { if (event.type === 'session:success') { this.session_id = event.body.id; this.connection.io.opts.query.session_id = event.body.id; if (this.application && event.body.pending_events) { event.body.pending_events.forEach((pending_event) => this.application._enqueueEvent(pending_event)); } } } /** * Disconnect from the cloud. * */ disconnect() { return this.connection.disconnect(); } /** * Connect to the cloud. * */ connect() { return this.connection.connect(); } /** * Get a connectivity report for all Vonage DCs and Media Servers. * @param {string} token - the JSON Web Token (JWT) * @param {object} options - configure the connectivityReport * @param {Function} options.dcListCallback - a callback function to edit the list of datacenters before connectivity checks * @returns {Promise<Report>} * @example <caption>Get a connectivity report</caption> * * rtc.connectivityReport(token, { * dcListCallback: (dcList) => {...dcList, additionalDc} * }).then((report) => { * console.log(report); * }).catch((error) => { * console.log(error); * }); */ async connectivityReport(token, options) { const ip = !rtc_helper_1.default.isNode() ? await publicip_1.default.v4() : undefined; const report = { machineInfo: { ip }, connectivityReport: [] }; try { const { response } = await utils_1.default.networkRequest({ type: 'GET', url: `${this.config.nexmo_api_url}/v0.3/discovery/api`, token }); let dcList = response; if (options === null || options === void 0 ? void 0 : options.dcListCallback) { dcList = options.dcListCallback(dcList); } for (const dc in dcList) { const endpoint = dcList[dc].endpoint; const apiUrl = dcList[dc].https; const wsUrl = dcList[dc].ws; try { const httpRes = await utils_1.default._checkHttpConnectivity(apiUrl); const wsRes = await utils_1.default._checkWsConnectivity(wsUrl, this.config.path, this.config.socket_io); const mediaConnectionReport = await utils_1.default._checkMediaServers(token, endpoint, dc); const rep = { name: dc, signalConnectionReport: { https: httpRes, ws: wsRes, }, mediaConnectionReport }; report.connectivityReport.push(rep); } catch (error) { this.log.error(`Error generating report for ${dc}`, error); } } } catch (error) { this.log.error(`Error fetching nexmo servers information`, error); } return report; } /** * Return a list with the connection health of the media servers for a specific datacenter. * @returns {Promise<MediaConnectionReport[]>} * @param {string} token - the JSON Web Token (JWT) * @param {string} nexmo_api_url - url of the nexmo api to be called * @param {string} datacenter - datacenter of interest * @example <caption>Return a list with the connection health of the media servers</caption> * * rtc.checkMediaServers('nexmo-api-url','dc').then((responseArray) => { * console.log(responseArray); * }).catch((error) => { * console.log(error); * }); */ async checkMediaServers(token, nexmo_api_url, datacenter) { return await utils_1.default._checkMediaServers(token, nexmo_api_url, datacenter); } /** * Return the connection health of a single media server including possible connectionTime in ms. * @returns {Promise<MediaConnectionReport>} * @param {string} ip - ip address of the Media Server * @param {string} port - port number of the Media Server * @example <caption>Return the connection health of a single media server</caption> * * rtc.checkMediaConnectivity('ip-address','1').then((response) => { * console.log(`IP Address of media server: ${response.ip}`); * console.log(`Able to connect: ${response.canConnect}`); * console.log(`ConnectionTime in ms: ${resonse.connectionTime}`); * }).catch((error) => { * console.log(error); * }); */ async checkMediaConnectivity(ip, port) { return await utils_1.default._checkMediaConnectivity(ip, port); } } exports.default = NexmoClient; /** * Enum for NexmoClient disconnection reason. * @readonly * @enum {string} * @alias NexmoClient.DISCONNECT_REASON */ NexmoClient.DISCONNECT_REASON = { ClientDisconnected: 'ClientDisconnected', TokenExpired: 'TokenExpired', ConnectionError: 'ConnectionError' }; module.exports = NexmoClient;