UNPKG

workano-js-sdk

Version:

Workano Communications SDK - A modern JavaScript SDK for WebRTC and VoIP integration.

1,308 lines (1,307 loc) 463 kB
import * as __WEBPACK_EXTERNAL_MODULE_js_base64_4ca5b7bc__ from "js-base64"; import * as __WEBPACK_EXTERNAL_MODULE_moment__ from "moment"; import * as __WEBPACK_EXTERNAL_MODULE_jsrsasign__ from "jsrsasign"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_api_session_state_3832d665__ from "sip.js/lib/api/session-state"; import * as __WEBPACK_EXTERNAL_MODULE_events__ from "events"; import * as __WEBPACK_EXTERNAL_MODULE_json_to_graphql_query_lib_jsonToGraphQLQuery_86e5fe4e__ from "json-to-graphql-query/lib/jsonToGraphQLQuery"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_api_f9f0ade8__ from "sip.js/lib/api"; import "webrtc-adapter"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_api_user_agent_state_a04f4d58__ from "sip.js/lib/api/user-agent-state"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_core_messages_parser_9974076a__ from "sip.js/lib/core/messages/parser"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_core_messages_methods_constants_78ac14f1__ from "sip.js/lib/core/messages/methods/constants"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_grammar_uri_b7779ac2__ from "sip.js/lib/grammar/uri"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_api_user_agent_8471afcd__ from "sip.js/lib/api/user-agent"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_platform_web_modifiers_modifiers_2ed0685f__ from "sip.js/lib/platform/web/modifiers/modifiers"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_core_dialogs_session_dialog_042454ee__ from "sip.js/lib/core/dialogs/session-dialog"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_api_messager_c3a09f5d__ from "sip.js/lib/api/messager"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_api_registerer_state_6e58ddd6__ from "sip.js/lib/api/registerer-state"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_api_transport_state_2606bc5d__ from "sip.js/lib/api/transport-state"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_platform_web_session_description_handler_peer_connection_configuration_default_48affb33__ from "sip.js/lib/platform/web/session-description-handler/peer-connection-configuration-default"; import * as __WEBPACK_EXTERNAL_MODULE_getstats__ from "getstats"; import * as __WEBPACK_EXTERNAL_MODULE_sip_js_lib_platform_web_session_description_handler_session_description_handler_fdd90ea0__ from "sip.js/lib/platform/web/session-description-handler/session-description-handler"; import * as __WEBPACK_EXTERNAL_MODULE_sdp_transform_45b84e5d__ from "sdp-transform"; import * as __WEBPACK_EXTERNAL_MODULE_reconnecting_websocket_e103ba3a__ from "reconnecting-websocket"; import "libphonenumber-js"; var __webpack_require__ = {}; (()=>{ __webpack_require__.d = function(exports, definition) { for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); }; })(); (()=>{ __webpack_require__.g = function() { if ('object' == typeof globalThis) return globalThis; try { return this || new Function('return this')(); } catch (e) { if ('object' == typeof window) return window; } }(); })(); (()=>{ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }; })(); (()=>{ __webpack_require__.r = function(exports) { if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); Object.defineProperty(exports, '__esModule', { value: true }); }; })(); var WebRTCPhone_namespaceObject = {}; __webpack_require__.r(WebRTCPhone_namespaceObject); __webpack_require__.d(WebRTCPhone_namespaceObject, { MESSAGE_TYPE_CHAT: ()=>MESSAGE_TYPE_CHAT, MESSAGE_TYPE_SIGNAL: ()=>MESSAGE_TYPE_SIGNAL, ON_AUDIO_STREAM: ()=>ON_AUDIO_STREAM, ON_CALL_ACCEPTED: ()=>ON_CALL_ACCEPTED, ON_CALL_ANSWERED: ()=>ON_CALL_ANSWERED, ON_CALL_CANCELED: ()=>ON_CALL_CANCELED, ON_CALL_ENDED: ()=>ON_CALL_ENDED, ON_CALL_ENDING: ()=>ON_CALL_ENDING, ON_CALL_ERROR: ()=>ON_CALL_ERROR, ON_CALL_FAILED: ()=>ON_CALL_FAILED, ON_CALL_HELD: ()=>ON_CALL_HELD, ON_CALL_INCOMING: ()=>ON_CALL_INCOMING, ON_CALL_MUTED: ()=>ON_CALL_MUTED, ON_CALL_OUTGOING: ()=>ON_CALL_OUTGOING, ON_CALL_REJECTED: ()=>ON_CALL_REJECTED, ON_CALL_RESUMED: ()=>ON_CALL_RESUMED, ON_CALL_UNHELD: ()=>ON_CALL_UNHELD, ON_CALL_UNMUTED: ()=>ON_CALL_UNMUTED, ON_CAMERA_DISABLED: ()=>ON_CAMERA_DISABLED, ON_CAMERA_RESUMED: ()=>ON_CAMERA_RESUMED, ON_CHAT: ()=>ON_CHAT, ON_DISCONNECTED: ()=>ON_DISCONNECTED, ON_EARLY_MEDIA: ()=>ON_EARLY_MEDIA, ON_MESSAGE: ()=>ON_MESSAGE, ON_MESSAGE_TRACK_UPDATED: ()=>ON_MESSAGE_TRACK_UPDATED, ON_NETWORK_STATS: ()=>ON_NETWORK_STATS, ON_PLAY_HANGUP_SOUND: ()=>ON_PLAY_HANGUP_SOUND, ON_PLAY_INBOUND_CALL_SIGNAL_SOUND: ()=>ON_PLAY_INBOUND_CALL_SIGNAL_SOUND, ON_PLAY_PROGRESS_SOUND: ()=>ON_PLAY_PROGRESS_SOUND, ON_PLAY_RING_SOUND: ()=>ON_PLAY_RING_SOUND, ON_PROGRESS: ()=>ON_PROGRESS, ON_REGISTERED: ()=>ON_REGISTERED, ON_REINVITE: ()=>ON_REINVITE, ON_REMOVE_STREAM: ()=>ON_REMOVE_STREAM, ON_SHARE_SCREEN_ENDED: ()=>ON_SHARE_SCREEN_ENDED, ON_SHARE_SCREEN_ENDING: ()=>ON_SHARE_SCREEN_ENDING, ON_SHARE_SCREEN_STARTED: ()=>ON_SHARE_SCREEN_STARTED, ON_SIGNAL: ()=>ON_SIGNAL, ON_TERMINATE_SOUND: ()=>ON_TERMINATE_SOUND, ON_TRACK: ()=>ON_TRACK, ON_UNREGISTERED: ()=>ON_UNREGISTERED, ON_USER_AGENT: ()=>ON_USER_AGENT, ON_VIDEO_INPUT_CHANGE: ()=>ON_VIDEO_INPUT_CHANGE, ON_VIDEO_STREAM: ()=>ON_VIDEO_STREAM, default: ()=>WebRTCPhone, events: ()=>WebRTCPhone_events }); var websocket_client_namespaceObject = {}; __webpack_require__.r(websocket_client_namespaceObject); __webpack_require__.d(websocket_client_namespaceObject, { AGENT_PAUSED: ()=>AGENT_PAUSED, AGENT_STATUS_UPDATE: ()=>AGENT_STATUS_UPDATE, AGENT_UNPAUSED: ()=>AGENT_UNPAUSED, APPLICATION_CALL_ANSWERED: ()=>APPLICATION_CALL_ANSWERED, APPLICATION_CALL_DELETED: ()=>APPLICATION_CALL_DELETED, APPLICATION_CALL_DTMF_RECEIVED: ()=>APPLICATION_CALL_DTMF_RECEIVED, APPLICATION_CALL_ENTERED: ()=>APPLICATION_CALL_ENTERED, APPLICATION_CALL_INITIATED: ()=>APPLICATION_CALL_INITIATED, APPLICATION_CALL_UPDATED: ()=>APPLICATION_CALL_UPDATED, APPLICATION_DESTINATION_NODE_CREATED: ()=>APPLICATION_DESTINATION_NODE_CREATED, APPLICATION_NODE_CREATED: ()=>APPLICATION_NODE_CREATED, APPLICATION_NODE_DELETED: ()=>APPLICATION_NODE_DELETED, APPLICATION_NODE_UPDATED: ()=>APPLICATION_NODE_UPDATED, APPLICATION_PLAYBACK_CREATED: ()=>APPLICATION_PLAYBACK_CREATED, APPLICATION_PLAYBACK_DELETED: ()=>APPLICATION_PLAYBACK_DELETED, APPLICATION_PROGRESS_STARTED: ()=>APPLICATION_PROGRESS_STARTED, APPLICATION_PROGRESS_STOPPED: ()=>APPLICATION_PROGRESS_STOPPED, APPLICATION_SNOOP_CREATED: ()=>APPLICATION_SNOOP_CREATED, APPLICATION_SNOOP_DELETED: ()=>APPLICATION_SNOOP_DELETED, APPLICATION_SNOOP_UPDATED: ()=>APPLICATION_SNOOP_UPDATED, APPLICATION_USER_OUTGOING_CALL_CREATED: ()=>APPLICATION_USER_OUTGOING_CALL_CREATED, AUTH_SESSION_EXPIRE_SOON: ()=>AUTH_SESSION_EXPIRE_SOON, AUTH_USER_EXTERNAL_AUTH_ADDED: ()=>AUTH_USER_EXTERNAL_AUTH_ADDED, AUTH_USER_EXTERNAL_AUTH_DELETED: ()=>AUTH_USER_EXTERNAL_AUTH_DELETED, CALL_ANSWERED: ()=>CALL_ANSWERED, CALL_CREATED: ()=>CALL_CREATED, CALL_DTMF_CREATED: ()=>CALL_DTMF_CREATED, CALL_ENDED: ()=>CALL_ENDED, CALL_HELD: ()=>CALL_HELD, CALL_LOG_USER_CREATED: ()=>CALL_LOG_USER_CREATED, CALL_RESUMED: ()=>CALL_RESUMED, CALL_UPDATED: ()=>CALL_UPDATED, CHATD_PRESENCE_UPDATED: ()=>CHATD_PRESENCE_UPDATED, CHATD_USER_ROOM_CREATED: ()=>CHATD_USER_ROOM_CREATED, CHATD_USER_ROOM_MESSAGE_CREATED: ()=>CHATD_USER_ROOM_MESSAGE_CREATED, CHAT_MESSAGE_RECEIVED: ()=>CHAT_MESSAGE_RECEIVED, CHAT_MESSAGE_SENT: ()=>CHAT_MESSAGE_SENT, CONFERENCE_ADHOC_DELETED: ()=>CONFERENCE_ADHOC_DELETED, CONFERENCE_ADHOC_PARTICIPANT_LEFT: ()=>CONFERENCE_ADHOC_PARTICIPANT_LEFT, CONFERENCE_USER_PARTICIPANT_JOINED: ()=>CONFERENCE_USER_PARTICIPANT_JOINED, CONFERENCE_USER_PARTICIPANT_LEFT: ()=>CONFERENCE_USER_PARTICIPANT_LEFT, CONFERENCE_USER_PARTICIPANT_TALK_STARTED: ()=>CONFERENCE_USER_PARTICIPANT_TALK_STARTED, CONFERENCE_USER_PARTICIPANT_TALK_STOPPED: ()=>CONFERENCE_USER_PARTICIPANT_TALK_STOPPED, ENDPOINT_STATUS_UPDATE: ()=>ENDPOINT_STATUS_UPDATE, FAVORITE_ADDED: ()=>FAVORITE_ADDED, FAVORITE_DELETED: ()=>FAVORITE_DELETED, FAX_OUTBOUND_USER_CREATED: ()=>FAX_OUTBOUND_USER_CREATED, FAX_OUTBOUND_USER_FAILED: ()=>FAX_OUTBOUND_USER_FAILED, FAX_OUTBOUND_USER_SUCCEEDED: ()=>FAX_OUTBOUND_USER_SUCCEEDED, HEARTBEAT_ENGINE_VERSION: ()=>HEARTBEAT_ENGINE_VERSION, LINE_STATUS_UPDATED: ()=>LINE_STATUS_UPDATED, MEETING_USER_GUEST_AUTHORIZATION_CREATED: ()=>MEETING_USER_GUEST_AUTHORIZATION_CREATED, MEETING_USER_PARTICIPANT_JOINED: ()=>MEETING_USER_PARTICIPANT_JOINED, MEETING_USER_PARTICIPANT_LEFT: ()=>MEETING_USER_PARTICIPANT_LEFT, MEETING_USER_PROGRESS: ()=>MEETING_USER_PROGRESS, SOCKET_EVENTS: ()=>SOCKET_EVENTS, SWITCHBOARD_HELD_CALLS_UPDATED: ()=>SWITCHBOARD_HELD_CALLS_UPDATED, SWITCHBOARD_HELD_CALL_ANSWERED: ()=>SWITCHBOARD_HELD_CALL_ANSWERED, SWITCHBOARD_QUEUED_CALLS_UPDATED: ()=>SWITCHBOARD_QUEUED_CALLS_UPDATED, SWITCHBOARD_QUEUED_CALL_ANSWERED: ()=>SWITCHBOARD_QUEUED_CALL_ANSWERED, TRUNK_STATUS_UPDATED: ()=>TRUNK_STATUS_UPDATED, USERS_FORWARDS_BUSY_UPDATED: ()=>USERS_FORWARDS_BUSY_UPDATED, USERS_FORWARDS_NOANSWER_UPDATED: ()=>USERS_FORWARDS_NOANSWER_UPDATED, USERS_FORWARDS_UNCONDITIONAL_UPDATED: ()=>USERS_FORWARDS_UNCONDITIONAL_UPDATED, USERS_SERVICES_DND_UPDATED: ()=>USERS_SERVICES_DND_UPDATED, USER_STATUS_UPDATE: ()=>USER_STATUS_UPDATE, USER_VOICEMAIL_MESSAGE_CREATED: ()=>USER_VOICEMAIL_MESSAGE_CREATED, USER_VOICEMAIL_MESSAGE_DELETED: ()=>USER_VOICEMAIL_MESSAGE_DELETED, USER_VOICEMAIL_MESSAGE_UPDATED: ()=>USER_VOICEMAIL_MESSAGE_UPDATED, default: ()=>websocket_client }); class BadResponse extends Error { static fromResponse(error, status) { return new BadResponse(error.message || JSON.stringify(error), status, error.timestamp, error.error_id, error.details, error); } static fromText(response, status) { return new BadResponse(response, status); } status; timestamp; errorId; details; error; constructor(message, status, timestamp = null, errorId = null, details = null, error = null){ super(message); this.timestamp = timestamp; this.status = status; this.errorId = errorId; this.details = details; this.error = error; } } class ServerError extends BadResponse { static fromResponse(error, status) { return new ServerError(error.message || JSON.stringify(error), status, error.timestamp, error.error_id, error.details); } static fromText(response, status) { return new ServerError(response, status); } } const isMobile_isMobile = ()=>'undefined' != typeof navigator && 'ReactNative' === navigator.product; const utils_isMobile = isMobile_isMobile; const camelToUnderscore = (key)=>{ if ('string' != typeof key) throw new Error('Input is not a string'); return key.charAt(0).toLowerCase() + key.substring(1).replace(/([A-Z])/g, '_$1').toLowerCase(); }; const obfuscateToken = (token)=>{ if (!token) return token; return token.split('-').map((part, index)=>0 === index ? part : 'xxxxxx').join('-'); }; const TRACE = 'trace'; const DEBUG = 'debug'; const INFO = 'info'; const LOG = 'log'; const WARN = 'warn'; const ERROR = 'error'; const CONSOLE_METHODS = [ INFO, LOG, WARN, ERROR ]; const LOG_LEVELS = [ TRACE, DEBUG, INFO, LOG, WARN, ERROR ]; const CATEGORY_PREFIX = 'logger-category='; const MAX_REMOTE_RETRY = 10; const addLevelsTo = (instance, withMethods = false)=>{ instance.TRACE = TRACE; instance.DEBUG = DEBUG; instance.INFO = INFO; instance.LOG = LOG; instance.WARN = WARN; instance.ERROR = ERROR; if (withMethods) { instance.trace = (...args)=>instance.apply(null, [ TRACE, ...args ]); instance.debug = (...args)=>instance.apply(null, [ DEBUG, ...args ]); instance.info = (...args)=>instance.apply(null, [ INFO, ...args ]); instance.log = (...args)=>instance.apply(null, [ LOG, ...args ]); instance.warn = (...args)=>instance.apply(null, [ WARN, ...args ]); instance.error = (...args)=>instance.apply(null, [ ERROR, ...args ]); } return instance; }; const safeStringify = (object)=>{ const result = '{"message": "Not parsable JSON"}'; try { return JSON.stringify(object); } catch (e) {} return result; }; class IssueReporter_IssueReporter { TRACE; INFO; LOG; WARN; ERROR; oldConsoleMethods; enabled; remoteClientConfiguration; buffer; bufferTimeout; _boundProcessBuffer; _boundParseLoggerBody; _callback; constructor(){ addLevelsTo(this); this.oldConsoleMethods = null; this.enabled = false; this.remoteClientConfiguration = null; this.buffer = []; this.bufferTimeout = null; this._boundProcessBuffer = this._processBuffer.bind(this); this._boundParseLoggerBody = this._parseLoggerBody.bind(this); this._callback = null; } init() { this._catchConsole(); } setCallback(cb) { this._callback = cb; } configureRemoteClient(configuration = { tag: 'wazo-sdk', host: null, port: null, level: null, extra: {} }) { this.remoteClientConfiguration = configuration; } enable() { if (!this.oldConsoleMethods) this.init(); this.enabled = true; } disable() { this.enabled = false; } loggerFor(category) { const logger = (level, ...args)=>{ this.log.apply(this, [ level, this._makeCategory(category), ...args ]); }; return addLevelsTo(logger, true); } removeSlashes(str) { return str.replace(/"/g, "'").replace(/\\/g, ''); } obfuscateHeaderToken(headers) { const newHeaders = { ...headers }; if ('X-Auth-Token' in newHeaders) newHeaders['X-Auth-Token'] = obfuscateToken(newHeaders['X-Auth-Token']); return newHeaders; } log(level, ...args) { if (!this.enabled) return; let category = null; let skipSendToRemote = false; let extra = {}; if (0 === args[0].indexOf(CATEGORY_PREFIX)) { category = args[0].split('=')[1]; args.splice(0, 1); } const lastArg = args[args.length - 1]; if (lastArg && ('object' == typeof lastArg && Object.keys(lastArg).length || lastArg instanceof Error)) { extra = lastArg instanceof Error ? { errorMessage: lastArg.message, errorStack: lastArg.stack, errorType: lastArg.constructor.name, skipSendToRemote: lastArg.skipSendToRemote } : lastArg; args.splice(1, 1); } if (extra.skipSendToRemote) { skipSendToRemote = true; delete extra.skipSendToRemote; } const date = (0, __WEBPACK_EXTERNAL_MODULE_moment__["default"])().format('YYYY-MM-DD HH:mm:ss.SSS'); const message = args.join(', '); let consoleMessage = message; if (Object.keys(extra).length) { const parsedExtra = safeStringify(extra); consoleMessage = `${consoleMessage} (${parsedExtra})`; } if (category) consoleMessage = `[${category}] ${consoleMessage}`; const consoleLevel = utils_isMobile() && 'error' === level ? WARN : level; const oldMethod = this.oldConsoleMethods?.[consoleLevel] || this.oldConsoleMethods?.log; oldMethod.apply(oldMethod, [ date, consoleMessage ]); if (this._callback) this._callback(level, consoleMessage); if (!skipSendToRemote) this._sendToRemoteLogger(level, { date, message, category, ...extra }); } logRequest(url, options, response, start) { if (!this.enabled) return; const { status } = response; const duration = +new Date() - +start; let level = TRACE; if (status >= 400 && status < 500) level = WARN; else if (status >= 500) level = ERROR; this.log(level, this._makeCategory('http'), url, { status, body: this.removeSlashes(JSON.stringify(options.body)), method: options.method, headers: this.obfuscateHeaderToken(options.headers), duration }); } getLogs() { console.warn('IssueReporter\'s logs aren\'t stored anymore. Please use fluentd to store them'); return []; } getParsedLogs() { console.warn('IssueReporter\'s logs aren\'t stored anymore. Please use fluentd to store them'); return []; } getReport() { return this.getParsedLogs().join('\r\n'); } _catchConsole() { this.oldConsoleMethods = {}; CONSOLE_METHODS.forEach((methodName)=>{ if (this.oldConsoleMethods) this.oldConsoleMethods[methodName] = console[methodName]; const parent = 'undefined' != typeof window ? window : __webpack_require__.g; parent.console[methodName] = (...args)=>{ try { this.log(methodName, args.join(' ')); if (this.oldConsoleMethods) this.oldConsoleMethods[methodName].apply(null, args); } catch (e) {} }; }); } _sendToRemoteLogger(level, payload = {}) { if (!this.remoteClientConfiguration) return; const { level: minLevel, bufferSize } = this.remoteClientConfiguration; if (!minLevel || this._isLevelAbove(minLevel, level)) return; payload.level = level; if (bufferSize > 0) return this._addToBuffer(payload); this._sendDebugToGrafana(payload); } _parseLoggerBody(payload) { const { level } = payload; const { maxMessageSize, extra } = this.remoteClientConfiguration || {}; delete payload.level; if (maxMessageSize && 'string' == typeof payload.message && payload.message.length > maxMessageSize) payload.message = `${payload.message.substr(0, maxMessageSize)}...`; return safeStringify({ level, ...payload, ...extra }); } _addToBuffer(payload) { if (this.bufferTimeout) { clearTimeout(this.bufferTimeout); this.bufferTimeout = null; } this.buffer.push(payload); const { bufferSize, bufferTimeout } = this.remoteClientConfiguration; if (this.buffer.length > bufferSize) return this._processBuffer(); if (bufferTimeout > 0) this.bufferTimeout = setTimeout(this._boundProcessBuffer, bufferTimeout); } _processBuffer() { this._sendDebugToGrafana(this.buffer); this.buffer = []; if (this.bufferTimeout) { clearTimeout(this.bufferTimeout); this.bufferTimeout = null; } } _computeRetryDelay(attempt, initial = 1000, maxWait = 50000) { const base = 1.5; const wait = Math.min(initial * base ** attempt, maxWait); const jitterWait = Math.max(initial, Math.random() * wait); return jitterWait; } _sendDebugToGrafana(payload, retry = 0) { if (!this.remoteClientConfiguration || retry >= MAX_REMOTE_RETRY) return; const { tag, host, port } = this.remoteClientConfiguration; const isSecure = 443 === +port; const url = `http${isSecure ? 's' : ''}://${host}${isSecure ? '' : `:${port}`}/${tag}`; const body = Array.isArray(payload) ? `[${payload.map(this._boundParseLoggerBody).join(',')}]` : this._parseLoggerBody(payload); fetch(url, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body }).catch((e)=>{ e.skipSendToRemote = true; this.log('error', this._makeCategory('grafana'), 'Sending log to grafana, error', e); const wait = this._computeRetryDelay(retry, 1000, 50000); setTimeout(()=>{ if (Array.isArray(payload)) payload = payload.map((message)=>this._writeRetryCount(message, retry + 1)); else if (payload && 'object' == typeof payload) payload = this._writeRetryCount(payload, retry + 1); this._sendDebugToGrafana(payload, retry + 1); }, wait); }); } _writeRetryCount(message, count) { if (message && 'object' == typeof message) message._retry = count; return message; } _isLevelAbove(level1, level2) { const index1 = LOG_LEVELS.indexOf(level1); const index2 = LOG_LEVELS.indexOf(level2); if (-1 === index1 || -1 === index2) return false; return index1 > index2; } _makeCategory(category) { return `${CATEGORY_PREFIX}${category}`; } } const IssueReporter = new IssueReporter_IssueReporter(); const methods = [ 'head', 'get', 'post', 'put', 'delete', 'options' ]; const api_requester_logger = IssueReporter ? IssueReporter.loggerFor('api') : console; const REQUEST_TIMEOUT_MS = 300000; class ApiRequester { server; agent; clientId; token; tenant; fetchOptions; refreshTokenCallback; refreshTokenPromise; shouldLogErrors; requestTimeout; head; get; post; put; delete; options; static successResponseParser(response) { return 204 === response.status || 201 === response.status || 200 === response.status; } static defaultParser(response) { return response.json().then((data)=>({ ...data, _headers: response.headers })); } static getQueryString(obj) { return Object.keys(obj).filter((key)=>obj[key]).map((key)=>`${key}=${encodeURIComponent(obj[key])}`).join('&'); } static base64Encode(str) { if ('undefined' != typeof btoa) try { return btoa(str); } catch (error) {} return __WEBPACK_EXTERNAL_MODULE_js_base64_4ca5b7bc__.Base64.encode(str); } constructor({ server, refreshTokenCallback, clientId, agent = null, token = null, fetchOptions, requestTimeout = REQUEST_TIMEOUT_MS }){ this.server = server; this.agent = agent; this.clientId = clientId; this.refreshTokenCallback = refreshTokenCallback; this.refreshTokenPromise = null; this.fetchOptions = fetchOptions || {}; this.requestTimeout = requestTimeout || REQUEST_TIMEOUT_MS; if (token) this.token = token; this.shouldLogErrors = true; methods.forEach((method)=>{ this[method] = function(...args) { args.splice(1, 0, method); return this.call.call(this, ...args); }; }); } setRequestTimeout(requestTimeout) { this.requestTimeout = requestTimeout; } setTenant(tenant) { this.tenant = tenant; } setToken(token) { this.token = token; } setFetchOptions(options) { this.fetchOptions = options; } disableErrorLogging() { this.shouldLogErrors = false; } enableErrorLogging() { this.shouldLogErrors = true; } async call(path, method = 'get', body = null, headers = null, parse = ApiRequester.defaultParser, firstCall = true) { const url = this.computeUrl(method, path, body); const newHeaders = this.getHeaders(headers); let newBody = 'get' === method ? null : body; if (newBody && 'application/json' === newHeaders['Content-Type']) newBody = JSON.stringify(newBody); const isHead = 'head' === method; const hasEmptyResponse = 'delete' === method || isHead; const newParse = hasEmptyResponse && parse === ApiRequester.defaultParser ? ApiRequester.successResponseParser : parse; const fetchOptions = { ...this.fetchOptions || {} }; const controller = new AbortController(); const extraHeaders = fetchOptions.headers || {}; delete fetchOptions.headers; const options = { method, body: newBody, signal: controller ? controller.signal : null, headers: { ...this.getHeaders(headers), ...extraHeaders }, agent: this.agent, ...fetchOptions }; if (this.refreshTokenPromise) { api_requester_logger.info('A token is already refreshing, waiting ...', { url }); await this.refreshTokenPromise; } const start = new Date(); const requestPromise = fetch(url, options).then((response)=>{ const contentType = response.headers.get('content-type') || ''; const isJson = -1 !== contentType.indexOf('application/json'); IssueReporter.logRequest(url, options, response, start); if (isHead && response.status >= 500 || !isHead && response.status >= 400) { const promise = isJson ? response.json() : response.text(); const exceptionClass = response.status >= 500 ? ServerError : BadResponse; return promise.then(async (err)=>{ if (firstCall && this._checkTokenExpired(response, err)) { api_requester_logger.warn('token expired', { error: err.reason }); return this._replayWithNewToken(err, path, method, body, headers, parse); } const error = 'string' == typeof err ? exceptionClass.fromText(err, response.status) : exceptionClass.fromResponse(err, response.status); if (this.shouldLogErrors) api_requester_logger.error('API error', error); throw error; }); } return newParse(response, isJson); }).catch((error)=>{ if (this.shouldLogErrors) api_requester_logger.error('Fetch failed', { url, options, message: error.message, stack: error.stack }); throw error; }); const requestTimeout = new Promise((resolve, reject)=>{ setTimeout(()=>{ controller.abort(); reject(new Error(`Request timed out after ${this.requestTimeout} ms`)); }, this.requestTimeout); }); return Promise.race([ requestPromise, requestTimeout ]); } _checkTokenExpired(response, err) { const isTokenNotFound = 404 === response.status && this._isTokenNotFound(err); return 401 === response.status || isTokenNotFound; } _isTokenNotFound(err) { return err && err.reason && 'No such token' === err.reason[0]; } _replayWithNewToken(err, path, method, body = null, headers = null, parse) { const isTokenNotFound = this._isTokenNotFound(err); let newPath = path; api_requester_logger.info('refreshing token', { inProgress: !!this.refreshTokenPromise }); this.refreshTokenPromise = this.refreshTokenPromise || this.refreshTokenCallback(); return this.refreshTokenPromise?.then(()=>{ this.refreshTokenPromise = null; api_requester_logger.info('token refreshed', { isTokenNotFound }); if (isTokenNotFound) { const pathParts = path.split('/'); pathParts.pop(); pathParts.push(this.token); newPath = pathParts.join('/'); } return this.call(newPath, method, body, headers, parse, false); }).catch((e)=>{ this.refreshTokenPromise = null; throw e; }); } getHeaders(header) { if (header instanceof Object) return header; return { 'X-Auth-Token': this.token, ...this.tenant ? { 'Wazo-Tenant': this.tenant } : null, Accept: 'application/json', 'Content-Type': 'application/json' }; } computeUrl(method, path, body) { const url = `${this.baseUrl}/${path}`; return 'get' === method && body && Object.keys(body).length ? `${url}?${ApiRequester.getQueryString(body)}` : url; } get baseUrl() { return `https://${this.server}/api`; } } const swarmPublicKey = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmkXOuNfY8u5xoTiIhkb8 djnbIwG/Wrz3vpo8BZir8L5e1a1nSy740qBjP7ZINBQoALDhFfmdOfJnCyEGiHuz ZW6jbG6C3PryE3Bu6GKwqSmD6k3q4Zk27fpwYAnNl+rWhYYM563rJZBda/INyHNN pK7M1mixWi7gNdjjXwoEXSSBx+VpYMkY6LiAB2mvHXTY9M1qI14dvgGoQISZQoKi NMTRCg5UP2ic0Dd9nSz/XpcOxGfa+0fwIl1F7RC1tJXOqvkGGPTOV4LLfg/Yta3h nUPX9EZZDIX6vO/0IBV1LzjSl2A1bYFYAjJfowv3i1CpvONBOClHjSY5t9Y8MH6p BwIDAQAB -----END PUBLIC KEY-----`; const pubkey = swarmPublicKey; const new_from = (instance, ToClass)=>{ const args = {}; Object.getOwnPropertyNames(instance).forEach((prop)=>{ args[prop] = instance[prop]; }); return new ToClass(args); }; class Line { type; id; extensions; endpointCustom; endpointSccp; endpointSip; static parse(plain) { return new Line({ id: plain.id, extensions: plain.extensions, endpointCustom: plain.endpoint_custom || null, endpointSccp: plain.endpoint_sccp || null, endpointSip: plain.endpoint_sip || null }); } static newFrom(profile) { return new_from(profile, Line); } is(line) { return !!line && this.id === line.id; } hasExtension(extension) { if (!this.extensions) return false; return this.extensions.some((ext)=>ext.exten === extension); } constructor({ id, extensions, endpointCustom, endpointSccp, endpointSip } = {}){ this.id = id; this.extensions = extensions; this.endpointCustom = endpointCustom || null; this.endpointSccp = endpointSccp || null; this.endpointSip = endpointSip || null; this.type = 'Line'; } } const FORWARD_KEYS = { BUSY: 'busy', NO_ANSWER: 'noanswer', UNCONDITIONAL: 'unconditional' }; class ForwardOption { destination; enabled; key; static parse(plain, key) { return new ForwardOption({ destination: plain.destination || '', enabled: plain.enabled, key }); } static newFrom(profile) { return new_from(profile, ForwardOption); } constructor({ destination, enabled, key } = {}){ this.destination = destination; this.enabled = enabled; this.key = key; } setDestination(number) { this.destination = number; return this; } setEnabled(enabled) { this.enabled = enabled; return this; } is(other) { return this.key === other.key; } } class Incall { id; extensions; extension; type = 'Incall'; constructor({ id, extensions } = {}){ this.id = id; this.extensions = extensions; this.extension = extensions?.[0]?.exten; } static parse(plain) { return new Incall({ id: plain.id, extensions: plain.extensions }); } static newFrom(profile) { return new_from(profile, Incall); } hasExtension(extension) { if (!this.extensions) return false; return this.extensions.some((ext)=>ext.exten === extension); } } const STATE = { AVAILABLE: 'available', UNAVAILABLE: 'unavailable', INVISIBLE: 'invisible', DISCONNECTED: 'disconnected', AWAY: 'away' }; const LINE_STATE = { AVAILABLE: 'available', HOLDING: 'holding', RINGING: 'ringing', TALKING: 'talking', UNAVAILABLE: 'unavailable', PROGRESSING: 'progressing' }; class Profile { id; firstName; lastName; email; lines; sipLines; incalls; username; mobileNumber; forwards; doNotDisturb; onlineCallRecordEnabled; state; ringSeconds; voicemail; status; subscriptionType; agent; switchboards; callPickupTargetUsers; static parse(plain) { return new Profile({ id: plain.uuid, firstName: plain.firstName || plain.firstname || '', lastName: plain.lastName || plain.lastname || '', email: plain.email, lines: plain.lines.map((line)=>Line.parse(line)), incalls: plain.incalls, username: plain.username, mobileNumber: plain.mobile_phone_number || '', ringSeconds: plain.ring_seconds, forwards: [ ForwardOption.parse(plain.forwards.unconditional, FORWARD_KEYS.UNCONDITIONAL), ForwardOption.parse(plain.forwards.noanswer, FORWARD_KEYS.NO_ANSWER), ForwardOption.parse(plain.forwards.busy, FORWARD_KEYS.BUSY) ], doNotDisturb: plain.services.dnd.enabled, subscriptionType: plain.subscription_type, voicemail: plain.voicemail, switchboards: plain.switchboards || [], agent: plain.agent, status: '', callPickupTargetUsers: plain.call_pickup_target_users || [], onlineCallRecordEnabled: plain.online_call_record_enabled }); } static newFrom(profile) { return new_from(profile, Profile); } constructor({ id, firstName, lastName, email, lines, username, mobileNumber, forwards, doNotDisturb, state, subscriptionType, voicemail, switchboards, agent, status, ringSeconds, sipLines, callPickupTargetUsers, onlineCallRecordEnabled, incalls }){ this.id = id; this.firstName = firstName; this.lastName = lastName; this.email = email; this.lines = lines; this.username = username; this.mobileNumber = mobileNumber; this.forwards = forwards; this.doNotDisturb = doNotDisturb; this.state = state; this.voicemail = voicemail; this.subscriptionType = subscriptionType; this.switchboards = switchboards; this.agent = agent; this.status = status; this.callPickupTargetUsers = callPickupTargetUsers; this.onlineCallRecordEnabled = onlineCallRecordEnabled; this.ringSeconds = ringSeconds; this.sipLines = sipLines || []; this.incalls = incalls?.map(Incall.parse) || []; } static getLinesState(lines) { let result = LINE_STATE.UNAVAILABLE; for (const line of lines){ if (line.state === LINE_STATE.RINGING) { result = LINE_STATE.RINGING; break; } if (line.state === LINE_STATE.TALKING) { result = LINE_STATE.TALKING; break; } if (line.state === LINE_STATE.AVAILABLE) result = LINE_STATE.AVAILABLE; } return result; } hasId(id) { return id === this.id; } setMobileNumber(number) { this.mobileNumber = number; return this; } setForwardOption(forwardOption) { const updatedForwardOptions = this.forwards.slice(); const index = updatedForwardOptions.findIndex((forward)=>forward.is(forwardOption)); updatedForwardOptions.splice(index, 1, forwardOption); this.forwards = updatedForwardOptions; return this; } setDoNotDisturb(enabled) { this.doNotDisturb = enabled; return this; } setState(state) { this.state = state; return this; } } const BACKEND = { OFFICE365: 'office365', PERSONAL: 'personal', GOOGLE: 'google', WAZO: 'wazo', CONFERENCE: 'conference', PHONEBOOK: 'phonebook' }; const SOURCE_MOBILE = 'mobile'; class Contact { static BACKEND = BACKEND; type; id; uuid; name; firstName; lastName; external; number; numbers; favorited; email; emails; entreprise; birthday; address; note; endpointId; personal; state; lineState; previousPresential; lastActivity; mobile; source; sourceId; status; backend; personalStatus; sessions; connected; doNotDisturb; ringing; lines; static merge(oldContacts, newContacts) { return newContacts.map((current)=>{ const old = oldContacts.find((contact)=>contact && contact.is(current)); return old ? current.merge(old) : current; }); } static sortContacts(a, b) { const aNames = a.separateName(); const bNames = b.separateName(); const aLastName = aNames.lastName; const bLastName = bNames.lastName; if (aLastName === bLastName) return aNames.firstName.localeCompare(bNames.firstName); return aLastName.localeCompare(bLastName); } static parseMany(response, offset = 0, limit = null) { if (!response || !response.results || 0 === limit) return []; const results = null !== limit && limit > 0 ? response.results.slice(offset, limit) : offset > 0 ? response.results.slice(offset) : response.results; return results.map((r)=>Contact.parse(r, response.column_types)); } static manyGraphQlWithNumbersParser(numbers) { return (response)=>{ if (!response.data || !response.data.me || !response.data.me.contacts) return []; return response.data.me.contacts.edges.map((edge, i)=>{ if (!edge.node) return null; const { email } = edge.node; const name = edge.node.firstname && edge.node.lastname ? `${edge.node.firstname || ''} ${edge.node.lastname || ''}` : edge.node.wazoReverse; return new Contact({ name, firstName: edge.node.firstname, lastName: edge.node.lastname, number: numbers[i], numbers: [ { label: 'primary', number: numbers[i] } ], backend: edge.node.wazoBackend, source: edge.node.wazoSourceName, sourceId: edge.node.wazoSourceEntryId || '', email: email || '', emails: email ? [ { label: 'primary', email } ] : [], uuid: edge.node.userUuid }); }).filter((contact)=>!!contact); }; } static fetchNumbers(plain, columns) { const numberColumns = columns.map((e, index)=>({ index, columnName: e })).filter((e)=>'number' === e.columnName || 'callable' === e.columnName).map((e)=>e.index); return plain.column_values.filter((e, index)=>numberColumns.some((i)=>i === index) && null !== e); } static parse(plain, columns) { const numbers = Contact.fetchNumbers(plain, columns); const email = plain.column_values[columns.indexOf('email')]; return new Contact({ name: plain.column_values[columns.indexOf('name')], firstName: plain.column_values[columns.indexOf('firstname')], lastName: plain.column_values[columns.indexOf('lastname')], number: numbers.length ? numbers[0] : '', numbers: numbers.map((number, i)=>({ label: 0 === i ? 'primary' : 'secondary', number })), favorited: plain.column_values[columns.indexOf('favorite')], email: email || '', emails: email ? [ { label: 'primary', email } ] : [], entreprise: plain.column_values[columns.indexOf('entreprise')] || '', birthday: plain.column_values[columns.indexOf('birthday')] || '', address: plain.column_values[columns.indexOf('address')] || '', note: plain.column_values[columns.indexOf('note')] || '', endpointId: plain.relations.endpoint_id, personal: plain.column_values[columns.indexOf('personal')], source: plain.source, sourceId: plain.relations.source_entry_id || '', uuid: plain.relations.user_uuid, backend: plain.backend || '' }); } static parseManyPersonal(results) { return results.map((r)=>Contact.parsePersonal(r)); } static parsePersonal(plain) { return new Contact({ name: `${plain.firstName || plain.firstname || ''} ${plain.lastName || plain.lastname || ''}`, firstName: plain.firstName || plain.firstname, lastName: plain.lastName || plain.lastname, number: plain.number || '', numbers: plain.number ? [ { label: 'primary', number: plain.number } ] : [], email: plain.email || '', emails: plain.email ? [ { label: 'primary', email: plain.email } ] : [], source: 'personal', uuid: plain.id, id: plain.id, sourceId: plain.id || '', entreprise: plain.entreprise || '', birthday: plain.birthday || '', address: plain.address || '', note: plain.note || '', favorited: plain.favorited, personal: true, backend: plain.backend || BACKEND.PERSONAL }); } static parseMobile(plain) { let address = ''; if (plain.postalAddresses.length) { const postalAddress = plain.postalAddresses[0]; address = `${postalAddress.street} ${postalAddress.city} ${postalAddress.postCode} ${postalAddress.country}`; } const firstName = plain.givenName || ''; const lastName = plain.familyName || ''; const companyName = plain.company || ''; const isCompanyAccount = !firstName && !lastName; const name = isCompanyAccount ? companyName : `${firstName} ${lastName}`; return new Contact({ name, firstName, lastName, number: plain.phoneNumbers.length ? plain.phoneNumbers[0].number : '', numbers: [ ...new Map(plain.phoneNumbers.map((item)=>[ item.number, item ])).values() ].map((item, idx)=>({ label: 0 === idx ? 'primary' : 'secondary', number: item.number })), email: plain.emailAddresses.length ? plain.emailAddresses[0].email : '', emails: plain.emailAddresses.length ? [ { label: 'primary', email: plain.emailAddresses[0].email } ] : [], source: SOURCE_MOBILE, sourceId: plain.recordID || '', birthday: plain.birthday ? `${plain.birthday.year}-${plain.birthday.month}-${plain.birthday.day}` : '', address, note: plain.note || '', favorited: false, personal: true }); } static parseManyOffice365(response, source) { return response.map((r)=>Contact.parseOffice365(r, source)); } static parseOffice365(single, source) { const emails = []; const numbers = []; if (single.emailAddresses) single.emailAddresses.map((email)=>emails.push({ email: email.address })); if (single.businessPhones) single.businessPhones.map((phone)=>numbers.push({ label: 'business', number: phone })); if (single.mobilePhone) numbers.push({ label: 'mobile', number: single.mobilePhone }); if (single.homePhones) single.homePhones.map((phone)=>numbers.push({ label: 'home', number: phone })); return new Contact({ sourceId: single.id || '', name: single.displayName || '', firstName: single.givenName, lastName: single.surname, number: numbers.length ? numbers[0].number : '', numbers, emails, source: source.name, backend: BACKEND.OFFICE365 }); } static parseManyGoogle(response, source) { return response.map((r)=>Contact.parseGoogle(r, source)); } static parseGoogle(single, source) { const emails = []; const numbers = []; if (single.emails) single.emails.forEach((email)=>'object' == typeof email ? { email: email.address, label: email.label } : { email }); if (single.numbers_by_label) Object.keys(single.numbers_by_label).forEach((label)=>numbers.push({ label, number: single.numbers_by_label[label] })); else if (single.numbers) single.numbers.forEach((phone)=>numbers.push({ number: phone })); return new Contact({ sourceId: single.id || '', name: single.name || '', firstName: single.lastname || '', lastName: single.firstname || '', number: numbers.length ? numbers[0].number : '', numbers, emails, source: source.name, backend: BACKEND.GOOGLE }); } static parseManyWazo(response, source) { return response.map((r)=>Contact.parseWazo(r, source)); } static parseWazo(single, source) { const emails = []; const numbers = []; if (single.email) emails.push({ label: 'email', email: single.email }); if (single.exten) numbers.push({ label: 'exten', number: single.exten }); if (single.mobile_phone_number) numbers.push({ label: 'mobile', number: single.mobile_phone_number }); return new Contact({ uuid: single.uuid, sourceId: String(single.id) || '', name: `${single.firstname}${single.lastname ? ` ${single.lastname}` : ''}`, firstName: single.firstname, lastName: single.lastname, number: numbers.length ? numbers[0].number : '', numbers, emails, source: source.name, backend: BACKEND.WAZO }); } static parseManyConference(response, source) { return response.map((r)=>Contact.parseConference(r, source)); } static parseConference(single, source) { const numbers = []; let firstNumber = ''; if (single && single.extensions && single.extensions.length > 0 && single.extensions[0].exten) { firstNumber = single.extensions[0].exten; numbers.push({ label: 'exten', number: firstNumber }); } return new Contact({ sourceId: String(single.id), name: single.name, number: firstNumber, numbers, source: source.name, backend: BACKEND.CONFERENCE }); } static newFrom(contact) { return new_from(contact, Contact); } constructor({ id, uuid, name, firstName, lastName, external, number, numbers, email, emails, source, sourceId, entreprise, birthday, address, note, state, lineState, lastActivity, mobile, status, endpointId, personal, favorited, backend, personalStatus, sessions, connected, doNotDisturb, ringing, previousPresential, lines } = {}){ this.id = id; this.uuid = uuid; this.name = name || ''; this.firstName = firstName || ''; this.lastName = lastName || ''; this.external = external || false; this.number = number; this.numbers = numbers; this.email = email; this.emails = emails; this.source = source; this.sourceId = sourceId; this.entreprise = entreprise; this.birthday = birthday; this.address = address; this.note = note; this.state = state; this.lineState = lineState;