From 576f645489c1693ee6dc75040f4bdff6fbc3335f Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Thu, 22 Apr 2021 14:39:54 -0400 Subject: [PATCH] snake case REST payloads, support for LCC with child_call_hook, handle 302 on outdial --- lib/http-routes/api/enqueue.js | 10 +- lib/session/adulting-call-session.js | 43 ++++ lib/session/call-session.js | 66 +++++- lib/tasks/dial.js | 50 ++++- lib/tasks/enqueue.js | 16 +- lib/tasks/gather.js | 1 + lib/tasks/say.js | 4 +- lib/utils/place-outdial.js | 23 ++- lib/utils/requestor.js | 10 +- package-lock.json | 289 ++++++++++++++------------- package.json | 7 +- 11 files changed, 340 insertions(+), 179 deletions(-) create mode 100644 lib/session/adulting-call-session.js diff --git a/lib/http-routes/api/enqueue.js b/lib/http-routes/api/enqueue.js index 9758497f..baf88c44 100644 --- a/lib/http-routes/api/enqueue.js +++ b/lib/http-routes/api/enqueue.js @@ -7,11 +7,13 @@ const {DbErrorUnprocessableRequest} = require('../utils/errors'); /** * validate the call state */ -function retrieveCallSession(callSid, opts) { +function retrieveCallSession(logger, callSid, opts) { + logger.debug(`retrieving session for callSid ${callSid}`); const cs = sessionTracker.get(callSid); if (cs) { const task = cs.currentTask; if (!task || task.name != TaskName.Enqueue) { + logger.debug({cs}, 'found call session but not in Enqueue task??'); throw new DbErrorUnprocessableRequest(`enqueue api failure: indicated call is not queued: ${task.name}`); } } @@ -19,14 +21,14 @@ function retrieveCallSession(callSid, opts) { } /** - * notify a waiting session that a conference has started + * notify a waiting session that a queue event has occurred */ router.post('/:callSid', async(req, res) => { const logger = req.app.locals.logger; const callSid = req.params.callSid; - logger.debug({body: req.body}, 'got enqueue event'); + logger.debug({callSid, body: req.body}, 'got enqueue event'); try { - const cs = retrieveCallSession(callSid, req.body); + const cs = retrieveCallSession(logger, callSid, req.body); if (!cs) { logger.info(`enqueue: callSid not found ${callSid}`); return res.sendStatus(404); diff --git a/lib/session/adulting-call-session.js b/lib/session/adulting-call-session.js new file mode 100644 index 00000000..cbd4725c --- /dev/null +++ b/lib/session/adulting-call-session.js @@ -0,0 +1,43 @@ +const CallSession = require('./call-session'); + +/** + * @classdesc Subclass of CallSession. Represents a CallSession + * that was initially a child call leg; i.e. established via a Dial verb. + * Now it is all grown up and filling out its own CallSession. Yoo-hoo! + * @extends CallSession + + */ +class AdultingCallSession extends CallSession { + constructor({logger, application, singleDialer, tasks, callInfo}) { + super({ + logger, + application, + srf: singleDialer.dlg.srf, + tasks, + callInfo + }); + this.sd = singleDialer; + + this.sd.dlg.on('destroy', () => { + this.logger.info('AdultingCallSession: called party hung up'); + this._callReleased(); + }); + this.sd.emit('adulting'); + } + + get dlg() { + return this.sd.dlg; + } + + get ep() { + return this.sd.ep; + } + + get callSid() { + return this.callInfo.callSid; + } + + +} + +module.exports = AdultingCallSession; diff --git a/lib/session/call-session.js b/lib/session/call-session.js index d9b728fe..ed296a97 100644 --- a/lib/session/call-session.js +++ b/lib/session/call-session.js @@ -45,7 +45,7 @@ class CallSession extends Emitter { this.serviceUrl = srf.locals.serviceUrl; } - if (!this.isConfirmCallSession && !this.isSmsCallSession) { + if (!this.isConfirmCallSession && !this.isSmsCallSession && !this.isAdultingCallSession) { sessionTracker.add(this.callSid, this); } } @@ -161,6 +161,12 @@ class CallSession extends Emitter { return this.application.transferredCall === true; } + /** + * returns true if this session is a ConfirmCallSession + */ + get isAdultingCallSession() { + return this.constructor.name === 'AdultingCallSession'; + } /** * returns true if this session is a ConfirmCallSession */ @@ -296,10 +302,47 @@ class CallSession extends Emitter { * @param {object} [opts.call_hook] - new call_status_hook */ async _lccCallHook(opts) { - const tasks = await this.requestor.request(opts.call_hook, this.callInfo); - if (tasks && tasks.length > 0) { - this.logger.info({tasks: listTaskNames(tasks)}, 'CallSession:updateCall new task list'); - this.replaceApplication(normalizeJambones(this.logger, tasks).map((tdata) => makeTask(this.logger, tdata))); + const webhooks = []; + let sd; + if (opts.call_hook) webhooks.push(this.requestor.request(opts.call_hook, this.callInfo)); + if (opts.child_call_hook) { + /* child call hook only allowed from a connected Dial state */ + const task = this.currentTask; + sd = task.sd; + if (task && TaskName.Dial === task.name && sd) { + webhooks.push(this.requestor.request(opts.child_call_hook, sd.callInfo)); + } + } + const [tasks1, tasks2] = await Promise.all(webhooks); + let tasks, childTasks; + if (opts.call_hook) { + tasks = tasks1; + if (opts.child_call_hook) childTasks = tasks2; + } + else childTasks = tasks1; + + if (childTasks) { + const {parentLogger} = this.srf.locals; + const childLogger = parentLogger.child({callId: this.callId, callSid: sd.callSid}); + const t = normalizeJambones(childLogger, childTasks).map((tdata) => makeTask(childLogger, tdata)); + childLogger.info({tasks: listTaskNames(t)}, 'CallSession:updateCall new task list for child call'); + const cs = await sd.doAdulting({ + logger: childLogger, + application: this.application, + tasks: t + }); + + /* need to update the callSid of the child with its own (new) AdultingCallSession */ + sessionTracker.add(cs.callSid, cs); + } + if (tasks) { + const t = normalizeJambones(this.logger, tasks).map((tdata) => makeTask(this.logger, tdata)); + this.logger.info({tasks: listTaskNames(t)}, 'CallSession:updateCall new task list'); + this.replaceApplication(t); + } + else { + /* we started a new app on the child leg, but nothing given for parent so hang him up */ + this.currentTask.kill(); } } @@ -404,7 +447,7 @@ class CallSession extends Emitter { if (opts.call_status) { return this._lccCallStatus(opts); } - if (opts.call_hook) { + if (opts.call_hook || opts.child_call_hook) { return await this._lccCallHook(opts); } if (opts.listen_status) { @@ -489,9 +532,16 @@ class CallSession extends Emitter { const ep = await this.ms.createEndpoint({remoteSdp: this.req.body}); ep.cs = this; this.ep = ep; - await ep.set('hangup_after_bridge', false); + ep.set({ + hangup_after_bridge: false, + park_after_bridge: true + }).catch((err) => this.logger.error({err}, 'Error setting park_after_bridge')); - this.logger.debug('allocated endpoint'); + this.logger.debug(`allocated endpoint ${this.ep.uuid}`); + + this.ep.on('destroy', () => { + this.logger.info(`endpoint was destroyed!! ${this.ep.uuid}`); + }); if (this.direction === CallDirection.Inbound) { if (task.earlyMedia && !this.req.finalResponseSent) { diff --git a/lib/tasks/dial.js b/lib/tasks/dial.js index c7e2b374..d4570474 100644 --- a/lib/tasks/dial.js +++ b/lib/tasks/dial.js @@ -113,6 +113,11 @@ class TaskDial extends Task { } get ep() { + /** + * Note: + * this.ep is the B leg-facing endpoint + * this.epOther is the A leg-facing endpoint + */ if (this.sd) return this.sd.ep; } @@ -133,6 +138,8 @@ class TaskDial extends Task { this._installDtmfDetection(cs, this.epOther, this.parentDtmfCollector); await this._attemptCalls(cs); await this.awaitTaskDone(); + + this.logger.debug({callSid: this.cs.callSid}, 'Dial:exec task is done, sending actionHook if any'); await this.performAction(this.results); this._removeDtmfDetection(cs, this.epOther); this._removeDtmfDetection(cs, this.ep); @@ -146,6 +153,8 @@ class TaskDial extends Task { super.kill(cs); this._removeDtmfDetection(this.cs, this.epOther); this._removeDtmfDetection(this.cs, this.ep); + this.logger.debug({callSid: this.cs.callSid}, 'Dial:kill removed dtmf listeners'); + this._killOutdials(); if (this.sd) { this.sd.kill(); @@ -212,6 +221,7 @@ class TaskDial extends Task { _removeDtmfDetection(cs, ep) { if (ep) { delete ep.dtmfDetector; + this.logger.debug(`Dial:_removeDtmfDetection endpoint ${ep.uuid}`); ep.removeAllListeners('dtmf'); } } @@ -219,13 +229,18 @@ class TaskDial extends Task { _onDtmf(cs, ep, evt) { if (ep.dtmfDetector) { const match = ep.dtmfDetector.keyPress(evt.dtmf); - const requestor = ep.dtmfDetector === this.parentDtmfCollector ? - cs.requestor : - this.sd.requestor; if (match) { - this.logger.debug(`parentCall triggered dtmf match: ${match}`); - requestor.request(this.dtmfHook, Object.assign({dtmf: match}, cs.callInfo)) - .catch((err) => this.logger.info(err, 'Dial:_onDtmf - error')); + this.logger.debug({callSid: this.cs.callSid}, `Dial:_onDtmf triggered dtmf match: ${match}`); + const requestor = ep.dtmfDetector === this.parentDtmfCollector ? + cs.requestor : + (this.sd ? this.sd.requestor : null); + if (!requestor) { + this.logger.info(`Dial:_onDtmf got digits on B leg after adulting: ${evt.dtmf}`); + } + else { + requestor.request(this.dtmfHook, Object.assign({dtmf: match}, cs.callInfo)) + .catch((err) => this.logger.info(err, 'Dial:_onDtmf - error')); + } } } } @@ -351,6 +366,16 @@ class TaskDial extends Task { this.logger.debug('Dial:_attemptCalls - all calls failed after decline, ending task'); this.kill(cs); } + }) + .once('adulting', () => { + /* child call just adulted and got its own session */ + this.logger.info('Dial:on_adulting: detaching child call leg'); + if (this.ep) { + this.logger.debug(`Dial:on_adulting: removing dtmf from ${this.ep.uuid}`); + this.ep.removeAllListeners('dtmf'); + } + this.sd = null; + this.callSid = null; }); } catch (err) { this.logger.error(err, 'Dial:_attemptCalls'); @@ -399,11 +424,14 @@ class TaskDial extends Task { } sessionTracker.add(this.callSid, cs); this.dlg.on('destroy', () => { - this.logger.debug('Dial:_selectSingleDial called party hungup, ending dial operation'); - sessionTracker.remove(this.callSid); - if (this.timerMaxCallDuration) clearTimeout(this.timerMaxCallDuration); - this.ep.unbridge(); - this.kill(cs); + /* if our child is adulting, he's own his own now.. */ + if (this.dlg) { + this.logger.debug('Dial:_selectSingleDial called party hungup, ending dial operation'); + sessionTracker.remove(this.callSid); + if (this.timerMaxCallDuration) clearTimeout(this.timerMaxCallDuration); + this.ep.unbridge(); + this.kill(cs); + } }); Object.assign(this.results, { diff --git a/lib/tasks/enqueue.js b/lib/tasks/enqueue.js index 58cbf2b2..65f3267b 100644 --- a/lib/tasks/enqueue.js +++ b/lib/tasks/enqueue.js @@ -275,18 +275,19 @@ class TaskEnqueue extends Task { this.logger.error({err}, `TaskEnqueue:_playHook error retrieving list info for queue ${this.queueName}`); } const json = await cs.application.requestor.request(hook, params); + const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata)); - const allowedTasks = json.filter((task) => allowed.includes(task.verb)); - if (json.length !== allowedTasks.length) { - this.logger.debug({json, allowedTasks}, 'unsupported task'); - throw new Error(`unsupported verb in dial enqueue waitHook: only ${JSON.stringify(allowed)}`); + const allowedTasks = tasks.filter((t) => allowed.includes(t.verb)); + if (tasks.length !== allowedTasks.length) { + this.logger.debug({tasks, allowedTasks}, 'unsupported task'); + throw new Error(`unsupported verb in enqueue waitHook: only ${JSON.stringify(allowed)}`); } - this.logger.debug(`TaskEnqueue:_playHook: executing ${json.length} tasks`); + this.logger.debug(`TaskEnqueue:_playHook: executing ${tasks.length} tasks`); // check for 'leave' verb and only execute tasks up till then const tasksToRun = []; let leave = false; - for (const o of json) { + for (const o of tasks) { if (o.verb === TaskName.Leave) { leave = true; this.logger.info('waitHook returned a leave task'); @@ -297,14 +298,13 @@ class TaskEnqueue extends Task { if (this.killed) return []; else if (tasksToRun.length > 0) { - const tasks = normalizeJambones(this.logger, tasksToRun).map((tdata) => makeTask(this.logger, tdata)); this._playSession = new ConfirmCallSession({ logger: this.logger, application: cs.application, dlg, ep: cs.ep, callInfo: cs.callInfo, - tasks + tasksToRun }); await this._playSession.exec(); this._playSession = null; diff --git a/lib/tasks/gather.js b/lib/tasks/gather.js index 0446d441..954505ed 100644 --- a/lib/tasks/gather.js +++ b/lib/tasks/gather.js @@ -91,6 +91,7 @@ class TaskGather extends Task { kill(cs) { super.kill(cs); this._killAudio(); + this.ep.removeAllListeners('dtmf'); this._resolve('killed'); } diff --git a/lib/tasks/say.js b/lib/tasks/say.js index 17b1834c..a1419af4 100644 --- a/lib/tasks/say.js +++ b/lib/tasks/say.js @@ -34,11 +34,13 @@ class TaskSay extends Task { }))) .filter((fp) => fp && fp.length); - this.logger.debug({files}, 'synthesized files for tts'); + this.logger.debug({files, loop: this.loop}, 'synthesized files for tts'); + if (!this.ep.connected) this.logger.debug('say: endpoint is not connected!'); while (!this.killed && this.loop-- && this.ep.connected) { let segment = 0; do { + this.logger.debug(`playing file ${files[segment]}`); await ep.play(files[segment]); } while (!this.killed && ++segment < files.length); } diff --git a/lib/utils/place-outdial.js b/lib/utils/place-outdial.js index 966467b6..806e0637 100644 --- a/lib/utils/place-outdial.js +++ b/lib/utils/place-outdial.js @@ -7,6 +7,7 @@ const assert = require('assert'); const ConfirmCallSession = require('../session/confirm-call-session'); const selectSbc = require('./select-sbc'); const Registrar = require('@jambonz/mw-registrar'); +const AdultingCallSession = require('../session/adulting-call-session'); const registrar = new Registrar({ host: process.env.JAMBONES_REDIS_HOST, port: process.env.JAMBONES_REDIS_PORT || 6379 @@ -137,7 +138,7 @@ class SingleDialer extends Emitter { localSdp: this.ep.local.sdp }); if (this.target.auth) opts.auth = this.target.auth; - this.dlg = await srf.createUAC(uri, opts, { + this.dlg = await srf.createUAC(uri, {...opts, followRedirects: true, keepUriOnRedirect: true}, { cbRequest: (err, req) => { if (err) { this.logger.error(err, 'SingleDialer:exec Error creating call'); @@ -266,7 +267,7 @@ class SingleDialer extends Emitter { // now execute it in a new ConfirmCallSession this.logger.debug(`SingleDialer:_executeApp: executing ${tasks.length} tasks`); const cs = new ConfirmCallSession({ - logger: this.logger, + logger: this.baseLogger, application: this.application, dlg: this.dlg, ep: this.ep, @@ -284,6 +285,24 @@ class SingleDialer extends Emitter { } } + async doAdulting({logger, tasks, application}) { + this.logger = logger; + this.adulting = true; + this.emit('adulting'); + await this.ep.unbridge() + .catch((err) => this.logger.info({err}, 'SingleDialer:doAdulting - failed to unbridge ep')); + this.ep.play('silence_stream://1000'); + const cs = new AdultingCallSession({ + logger: this.logger, + singleDialer: this, + application, + callInfo: this.callInfo, + tasks + }); + cs.exec(); + return cs; + } + _notifyCallStatusChange({callStatus, sipStatus, duration}) { assert((typeof duration === 'number' && callStatus === CallStatus.Completed) || (!duration && callStatus !== CallStatus.Completed), diff --git a/lib/utils/requestor.js b/lib/utils/requestor.js index 1761a9c4..91a9edcc 100644 --- a/lib/utils/requestor.js +++ b/lib/utils/requestor.js @@ -1,7 +1,7 @@ const bent = require('bent'); const parseUrl = require('parse-url'); const assert = require('assert'); - +const snakeCaseKeys = require('snakecase-keys'); const toBase64 = (str) => Buffer.from(str || '', 'utf8').toString('base64'); function basicAuth(username, password) { @@ -62,7 +62,7 @@ class Requestor { * @param {object} [params] - request parameters */ async request(hook, params) { - params = params || null; + const payload = params ? snakeCaseKeys(params, {exclude: ['customerData', 'sip']}) : null; const url = hook.url || hook; const method = hook.method || 'POST'; const {username, password} = typeof hook === 'object' ? hook : {}; @@ -70,14 +70,14 @@ class Requestor { assert.ok(url, 'Requestor:request url was not provided'); assert.ok, (['GET', 'POST'].includes(method), `Requestor:request method must be 'GET' or 'POST' not ${method}`); - this.logger.debug({hook, params}, `Requestor:request ${method} ${url}`); + this.logger.debug({hook, payload}, `Requestor:request ${method} ${url}`); const startAt = process.hrtime(); let buf; try { buf = isRelativeUrl(url) ? - await this.post(url, params, this.authHeader) : - await bent(method, 'buffer', 200, 201, 202)(url, params, basicAuth(username, password)); + await this.post(url, payload, this.authHeader) : + await bent(method, 'buffer', 200, 201, 202)(url, payload, basicAuth(username, password)); } catch (err) { this.logger.info({baseUrl: this.baseUrl, url, statusCode: err.statusCode}, `web callback returned unexpected error code ${err.statusCode}`); diff --git a/package-lock.json b/package-lock.json index 62a92313..7392e173 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,9 @@ "version": "0.3.0", "license": "MIT", "dependencies": { - "@jambonz/db-helpers": "^0.5.10", + "@jambonz/db-helpers": "^0.5.20", "@jambonz/mw-registrar": "^0.1.9", - "@jambonz/realtimedb-helpers": "^0.4.0", + "@jambonz/realtimedb-helpers": "^0.4.1", "@jambonz/stats-collector": "^0.1.5", "aws-sdk": "^2.848.0", "bent": "^7.3.12", @@ -18,12 +18,13 @@ "debug": "^4.3.1", "deepcopy": "^2.1.0", "drachtio-fsmrf": "^2.0.7", - "drachtio-srf": "^4.4.47", + "drachtio-srf": "^4.4.48", "express": "^4.17.1", "ip": "^1.1.5", "moment": "^2.29.1", "parse-url": "^5.0.2", "pino": "^6.11.1", + "snakecase-keys": "^3.2.1", "uuid": "^8.3.2", "verify-aws-sns-signature": "^0.0.6", "xml2js": "^0.4.23" @@ -400,10 +401,11 @@ } }, "node_modules/@jambonz/db-helpers": { - "version": "0.5.11", - "resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.5.11.tgz", - "integrity": "sha512-3IV28+SD07AFPAf4a9BMjbBlPc4LOQ62uNdVO4hFY4rG7ttj/eWRYoKFItI0564MbSTHXtNyx1DsqHpI+60ZwA==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.5.20.tgz", + "integrity": "sha512-oMwujBu+twaSJxdAOnU9a6yhIZHLyAg/EwNbOjPUxofOPKOXV4Wma8ZtyWhWBvfdBLZfngE5pqIVkG6Uen/BTA==", "dependencies": { + "cidr-matcher": "^2.1.1", "debug": "^4.3.1", "mysql2": "^2.2.5", "uuid": "^8.3.2" @@ -419,31 +421,6 @@ "redis": "^3.0.2" } }, - "node_modules/@jambonz/mw-registrar/node_modules/redis": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz", - "integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==", - "dependencies": { - "denque": "^1.4.1", - "redis-commands": "^1.5.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@jambonz/mw-registrar/node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@jambonz/promisify-redis": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@jambonz/promisify-redis/-/promisify-redis-0.0.6.tgz", @@ -452,22 +429,17 @@ "redis-commands": "^1.6.0" } }, - "node_modules/@jambonz/promisify-redis/node_modules/redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, "node_modules/@jambonz/realtimedb-helpers": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.0.tgz", - "integrity": "sha512-MA+JFC6oV1DpgxtSgoZBMVCz5EJkSOxjV6c6OZRXv219gvMFwbvbMPnh4f7Fhry8jbwKA4CMl5HXwmY5fp7iEQ==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.1.tgz", + "integrity": "sha512-GDZ+VkVkSatAh6rYxapzi3r0k/Lj45mh1LAV6aB8U1kTcm3cD9u8QDBB04JL+seBg9mZmEB5aEiCdE9icOnJew==", "dependencies": { "@google-cloud/text-to-speech": "^3.1.3", "@jambonz/promisify-redis": "0.0.6", "@jambonz/stats-collector": "^0.1.5", "aws-sdk": "^2.840.0", "debug": "^4.3.1", - "redis": "^2.8.0" + "redis": "^3.0.0" } }, "node_modules/@jambonz/stats-collector": { @@ -1403,11 +1375,6 @@ "ignored": "bin/ignored" } }, - "node_modules/double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" - }, "node_modules/drachtio-fsmrf": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-2.0.7.tgz", @@ -1482,26 +1449,26 @@ } }, "node_modules/drachtio-sip": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.5.2.tgz", - "integrity": "sha512-vwHRQQuS12JKO5hFdZKD8cYPJKQA1EFd5KVpw1ZuVSizMLrxz3++CuWYwRMmH97vLVWG4FMGFWjlDO3S2fhtPw==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.5.3.tgz", + "integrity": "sha512-Ja4ToevqYCQj/8rIM7oEDfncypMxVVdvw1Zo471otfAxCXEzMokbVQGlADcp3lpFEmuWCo1ypE6hItVKe2z0dg==", "dependencies": { "debug": "^4.3.1", "only": "0.0.2", - "sip-message-examples": "0.0.5", "sip-status": "~0.1.0" } }, "node_modules/drachtio-srf": { - "version": "4.4.47", - "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.47.tgz", - "integrity": "sha512-dpbutyaRWj0llqRShkd02nsaLj0dlSaKJhG6IzxeLQSiyY6h5/vtqEDaw/uft/sSqwrMWwu+6SlkutKjb5ei0A==", + "version": "4.4.48", + "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.48.tgz", + "integrity": "sha512-+ocXNV7pVh7yntNSuC96mFp3uuCYxGBz05G+M6ik0DztLy6Ri5J9zA5JbOWAtSB8XIfsw0xxdPLQjJtlZq8D5Q==", + "license": "MIT", "dependencies": { "async": "^1.4.2", "debug": "^3.2.7", "delegates": "^0.1.0", "deprecate": "^1.1.1", - "drachtio-sip": "^0.5.2", + "drachtio-sip": "^0.5.3", "node-noop": "0.0.1", "only": "0.0.2", "sdp-transform": "^2.14.1", @@ -3233,6 +3200,17 @@ "node": ">=8" } }, + "node_modules/map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -3927,22 +3905,27 @@ "dev": true }, "node_modules/redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", "dependencies": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-redis" } }, "node_modules/redis-commands": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", - "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" }, "node_modules/redis-errors": { "version": "1.2.0", @@ -3953,11 +3936,14 @@ } }, "node_modules/redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "dependencies": { + "redis-errors": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, "node_modules/regexp.prototype.flags": { @@ -4229,11 +4215,6 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "node_modules/sip-message-examples": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/sip-message-examples/-/sip-message-examples-0.0.5.tgz", - "integrity": "sha512-p/t6su7UkiicfUR2CWlUO3rJJIeGO+6oekdnCp8QC6ut5z3AyfhjaefmJKWW8JyrC/2nZYmLdEfBqpKryS4QSg==" - }, "node_modules/sip-methods": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/sip-methods/-/sip-methods-0.3.0.tgz", @@ -4288,6 +4269,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/snakecase-keys": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.2.1.tgz", + "integrity": "sha512-CjU5pyRfwOtaOITYv5C8DzpZ8XA/ieRsDpr93HI2r6e3YInC6moZpSQbmUtg8cTk58tq2x3jcG2gv+p1IZGmMA==", + "dependencies": { + "map-obj": "^4.1.0", + "to-snake-case": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/sonic-boom": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.3.2.tgz", @@ -4737,6 +4730,27 @@ "node": ">=4" } }, + "node_modules/to-no-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", + "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" + }, + "node_modules/to-snake-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", + "integrity": "sha1-znRpE4l5RgGah+Yu366upMYIq4w=", + "dependencies": { + "to-space-case": "^1.0.0" + } + }, + "node_modules/to-space-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", + "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", + "dependencies": { + "to-no-case": "^1.0.0" + } + }, "node_modules/toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -5462,10 +5476,11 @@ "dev": true }, "@jambonz/db-helpers": { - "version": "0.5.11", - "resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.5.11.tgz", - "integrity": "sha512-3IV28+SD07AFPAf4a9BMjbBlPc4LOQ62uNdVO4hFY4rG7ttj/eWRYoKFItI0564MbSTHXtNyx1DsqHpI+60ZwA==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.5.20.tgz", + "integrity": "sha512-oMwujBu+twaSJxdAOnU9a6yhIZHLyAg/EwNbOjPUxofOPKOXV4Wma8ZtyWhWBvfdBLZfngE5pqIVkG6Uen/BTA==", "requires": { + "cidr-matcher": "^2.1.1", "debug": "^4.3.1", "mysql2": "^2.2.5", "uuid": "^8.3.2" @@ -5479,27 +5494,6 @@ "@jambonz/promisify-redis": "0.0.6", "debug": "^4.3.1", "redis": "^3.0.2" - }, - "dependencies": { - "redis": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz", - "integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==", - "requires": { - "denque": "^1.4.1", - "redis-commands": "^1.5.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - } - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - } } }, "@jambonz/promisify-redis": { @@ -5508,26 +5502,19 @@ "integrity": "sha512-9KmWV+ODDOPwdqijhgXOXkloGNm7nmCf3ch4D1vN46lh9FQLmnlQmEmBHIFaKB3vAgIgcMzNzcNmY23JGCGguA==", "requires": { "redis-commands": "^1.6.0" - }, - "dependencies": { - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - } } }, "@jambonz/realtimedb-helpers": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.0.tgz", - "integrity": "sha512-MA+JFC6oV1DpgxtSgoZBMVCz5EJkSOxjV6c6OZRXv219gvMFwbvbMPnh4f7Fhry8jbwKA4CMl5HXwmY5fp7iEQ==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.1.tgz", + "integrity": "sha512-GDZ+VkVkSatAh6rYxapzi3r0k/Lj45mh1LAV6aB8U1kTcm3cD9u8QDBB04JL+seBg9mZmEB5aEiCdE9icOnJew==", "requires": { "@google-cloud/text-to-speech": "^3.1.3", "@jambonz/promisify-redis": "0.0.6", "@jambonz/stats-collector": "^0.1.5", "aws-sdk": "^2.840.0", "debug": "^4.3.1", - "redis": "^2.8.0" + "redis": "^3.0.0" } }, "@jambonz/stats-collector": { @@ -6335,11 +6322,6 @@ "minimatch": "^3.0.4" } }, - "double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" - }, "drachtio-fsmrf": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-2.0.7.tgz", @@ -6406,26 +6388,25 @@ } }, "drachtio-sip": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.5.2.tgz", - "integrity": "sha512-vwHRQQuS12JKO5hFdZKD8cYPJKQA1EFd5KVpw1ZuVSizMLrxz3++CuWYwRMmH97vLVWG4FMGFWjlDO3S2fhtPw==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.5.3.tgz", + "integrity": "sha512-Ja4ToevqYCQj/8rIM7oEDfncypMxVVdvw1Zo471otfAxCXEzMokbVQGlADcp3lpFEmuWCo1ypE6hItVKe2z0dg==", "requires": { "debug": "^4.3.1", "only": "0.0.2", - "sip-message-examples": "0.0.5", "sip-status": "~0.1.0" } }, "drachtio-srf": { - "version": "4.4.47", - "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.47.tgz", - "integrity": "sha512-dpbutyaRWj0llqRShkd02nsaLj0dlSaKJhG6IzxeLQSiyY6h5/vtqEDaw/uft/sSqwrMWwu+6SlkutKjb5ei0A==", + "version": "4.4.48", + "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.48.tgz", + "integrity": "sha512-+ocXNV7pVh7yntNSuC96mFp3uuCYxGBz05G+M6ik0DztLy6Ri5J9zA5JbOWAtSB8XIfsw0xxdPLQjJtlZq8D5Q==", "requires": { "async": "^1.4.2", "debug": "^3.2.7", "delegates": "^0.1.0", "deprecate": "^1.1.1", - "drachtio-sip": "^0.5.2", + "drachtio-sip": "^0.5.3", "node-noop": "0.0.1", "only": "0.0.2", "sdp-transform": "^2.14.1", @@ -7846,6 +7827,11 @@ "semver": "^6.0.0" } }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -8405,19 +8391,20 @@ } }, "redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", "requires": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" } }, "redis-commands": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", - "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" }, "redis-errors": { "version": "1.2.0", @@ -8425,9 +8412,12 @@ "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" }, "redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } }, "regexp.prototype.flags": { "version": "1.3.1", @@ -8654,11 +8644,6 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "sip-message-examples": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/sip-message-examples/-/sip-message-examples-0.0.5.tgz", - "integrity": "sha512-p/t6su7UkiicfUR2CWlUO3rJJIeGO+6oekdnCp8QC6ut5z3AyfhjaefmJKWW8JyrC/2nZYmLdEfBqpKryS4QSg==" - }, "sip-methods": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/sip-methods/-/sip-methods-0.3.0.tgz", @@ -8706,6 +8691,15 @@ } } }, + "snakecase-keys": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.2.1.tgz", + "integrity": "sha512-CjU5pyRfwOtaOITYv5C8DzpZ8XA/ieRsDpr93HI2r6e3YInC6moZpSQbmUtg8cTk58tq2x3jcG2gv+p1IZGmMA==", + "requires": { + "map-obj": "^4.1.0", + "to-snake-case": "^1.0.0" + } + }, "sonic-boom": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.3.2.tgz", @@ -9094,6 +9088,27 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "to-no-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", + "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" + }, + "to-snake-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", + "integrity": "sha1-znRpE4l5RgGah+Yu366upMYIq4w=", + "requires": { + "to-space-case": "^1.0.0" + } + }, + "to-space-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", + "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", + "requires": { + "to-no-case": "^1.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", diff --git a/package.json b/package.json index c2f188a9..a0bb3c42 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,9 @@ "jslint": "eslint app.js lib" }, "dependencies": { - "@jambonz/db-helpers": "^0.5.10", + "@jambonz/db-helpers": "^0.5.20", "@jambonz/mw-registrar": "^0.1.9", - "@jambonz/realtimedb-helpers": "^0.4.0", + "@jambonz/realtimedb-helpers": "^0.4.1", "@jambonz/stats-collector": "^0.1.5", "aws-sdk": "^2.848.0", "bent": "^7.3.12", @@ -36,12 +36,13 @@ "debug": "^4.3.1", "deepcopy": "^2.1.0", "drachtio-fsmrf": "^2.0.7", - "drachtio-srf": "^4.4.47", + "drachtio-srf": "^4.4.48", "express": "^4.17.1", "ip": "^1.1.5", "moment": "^2.29.1", "parse-url": "^5.0.2", "pino": "^6.11.1", + "snakecase-keys": "^3.2.1", "uuid": "^8.3.2", "verify-aws-sns-signature": "^0.0.6", "xml2js": "^0.4.23"