Feature/app call count tracking (#51)

* add call count tracking at the app level (optional)

* add histogram for rtt to rtpengine commands

* update time-series

* update rtpengine-utils
This commit is contained in:
Dave Horton
2022-09-22 23:41:00 +02:00
committed by GitHub
parent 88bdeffe4b
commit d27847747d
6 changed files with 181 additions and 125 deletions

2
app.js
View File

@@ -16,6 +16,7 @@ const logger = require('pino')(opts);
const {
writeCallCount,
writeCallCountSP,
writeCallCountApp,
queryCdrs,
writeCdrs,
writeAlerts,
@@ -74,6 +75,7 @@ srf.locals = {...srf.locals,
stats,
writeCallCount,
writeCallCountSP,
writeCallCountApp,
queryCdrs,
writeCdrs,
writeAlerts,

View File

@@ -1,6 +1,13 @@
const Emitter = require('events');
const SrsClient = require('@jambonz/siprec-client-utils');
const {makeRtpEngineOpts, SdpWantsSrtp, makeAccountCallCountKey, makeSPCallCountKey} = require('./utils');
const {
makeRtpEngineOpts,
SdpWantsSrtp,
nudgeCallCounts,
roundTripTime,
parseConnectionIp
} = require('./utils');
const {forwardInDialogRequests} = require('drachtio-fn-b2b-sugar');
const {parseUri, stringifyUri, SipError} = require('drachtio-srf');
const debug = require('debug')('jambonz:sbc-inbound');
@@ -49,9 +56,11 @@ class CallSession extends Emitter {
this.activeCallIds = this.srf.locals.activeCallIds;
this.decrKey = req.srf.locals.realtimeDbHelpers.decrKey;
this.callCountKey = makeAccountCallCountKey(req.locals.account_sid);
this.callCountKeySP = makeSPCallCountKey(req.locals.service_provider_sid);
this._mediaReleased = false;
this.application_sid = req.locals.application_sid;
this.account_sid = req.locals.account_sid;
this.service_provider_sid = req.locals.service_provider_sid;
}
get isFromMSTeams() {
@@ -147,8 +156,13 @@ class CallSession extends Emitter {
direction: ['public', 'private'],
sdp
};
const startAt = process.hrtime();
const response = await this.offer(opts);
this.logger.debug({opts, response}, 'response from rtpengine to offer');
this.rtpengineIp = opts.sdp ? parseConnectionIp(opts.sdp) : 'undefined';
const rtt = roundTripTime(startAt);
this.stats.histogram('app.rtpengine.response_time', rtt, [
'direction:inbound', 'command:offer', `rtpengine:${this.rtpengineIp}`]);
this.logger.debug({opts, response, rtt, rtpengine: this.rtpengineIp}, 'response from rtpengine to offer');
if ('ok' !== response.result) {
this.logger.error({}, `rtpengine offer failed with ${JSON.stringify(response)}`);
throw new Error('rtpengine failed: answer');
@@ -223,7 +237,11 @@ class CallSession extends Emitter {
'to-tag': this.rtpEngineOpts.uac.tag,
sdp
};
const startAt = process.hrtime();
const response = await this.answer(opts);
const rtt = roundTripTime(startAt);
this.stats.histogram('app.rtpengine.response_time', rtt, [
'direction:inbound', 'command:answer', `rtpengine:${this.rtpengineIp}`]);
if ('ok' !== response.result) {
this.logger.error(`rtpengine answer failed with ${JSON.stringify(response)}`);
throw new Error('rtpengine failed: answer');
@@ -285,7 +303,7 @@ class CallSession extends Emitter {
this.stats.increment('sbc.terminations', tags);
this.activeCallIds.set(this.req.get('Call-ID'), this);
const call_sid = uac.res?.get('X-Call-Sid');
const application_sid = uac.res?.get('X-Application-Sid');
const application_sid = this.application_sid || uac.res?.get('X-Application-Sid');
if (this.req.locals.cdr) {
this.req.locals.cdr = {
...this.req.locals.cdr,
@@ -307,24 +325,19 @@ class CallSession extends Emitter {
await other.destroy();
} catch (err) {}
this.unsubscribeDTMF(this.logger, this.req.get('Call-ID'), this.rtpEngineOpts.uas.tag);
if (process.env.JAMBONES_HOSTING || process.env.JAMBONES_TRACK_ACCOUNT_CALLS) {
const {account_sid, service_provider_sid} = this.req.locals;
const {writeCallCount, writeCallCountSP} = this.req.srf.locals;
Promise.all([
this.decrKey(this.callCountKey),
this.decrKey(this.callCountKeySP)
])
.then(([calls, callsSP]) => {
this.logger.info({calls, callsSP}, 'decremented call counts after call completion');
return Promise.all([
writeCallCount(account_sid, calls),
writeCallCountSP(service_provider_sid, callsSP)
]);
})
.catch((err) => {
this.logger.error({err}, 'error decrementing call counts after call completion');
});
const trackingOn = process.env.JAMBONES_TRACK_ACCOUNT_CALLS ||
process.env.JAMBONES_TRACK_SP_CALLS ||
process.env.JAMBONES_TRACK_APP_CALLS;
if (process.env.JAMBONES_HOSTING || trackingOn) {
const {writeCallCount, writeCallCountSP, writeCallCountApp} = this.req.srf.locals;
await nudgeCallCounts(this.logger, {
service_provider_sid: this.service_provider_sid,
account_sid: this.account_sid,
application_sid: this.application_sid
}, this.decrKey, {writeCallCountSP, writeCallCount, writeCallCountApp})
.catch((err) => this.logger.error(err, 'Error decrementing call counts'));
}
/* write cdr for connected call */

View File

@@ -2,7 +2,7 @@ const debug = require('debug')('jambonz:sbc-inbound');
const assert = require('assert');
const Emitter = require('events');
const parseUri = require('drachtio-srf').parseUri;
const {makeAccountCallCountKey, makeSPCallCountKey} = require('./utils');
const {nudgeCallCounts} = require('./utils');
const msProxyIps = process.env.MS_TEAMS_SIP_PROXY_IPS ?
process.env.MS_TEAMS_SIP_PROXY_IPS.split(',').map((i) => i.trim()) :
[];
@@ -226,6 +226,7 @@ module.exports = function(srf, logger) {
service_provider_sid: account.service_provider_sid,
account_sid: account.account_sid,
account,
application_sid: account.device_calling_application_sid,
webhook_secret: account.webhook_secret,
...req.locals
};
@@ -264,38 +265,36 @@ module.exports = function(srf, logger) {
};
const checkLimits = async(req, res, next) => {
if (!process.env.JAMBONES_HOSTING && !process.env.JAMBONES_TRACK_ACCOUNT_CALLS) return next(); // skip
const trackingOn = process.env.JAMBONES_TRACK_ACCOUNT_CALLS ||
process.env.JAMBONES_TRACK_SP_CALLS ||
process.env.JAMBONES_TRACK_APP_CALLS;
if (!process.env.JAMBONES_HOSTING && !trackingOn) return next(); // skip
const {incrKey, decrKey} = req.srf.locals.realtimeDbHelpers;
const {logger, account_sid, account, service_provider_sid} = req.locals;
const {writeCallCount, writeCallCountSP, writeAlerts, AlertType} = req.srf.locals;
const {logger, account_sid, account, service_provider_sid, application_sid} = req.locals;
const {writeCallCount, writeCallCountSP, writeCallCountApp, writeAlerts, AlertType} = req.srf.locals;
assert(account_sid);
assert(service_provider_sid);
const keyAccount = makeAccountCallCountKey(account_sid);
const keySP = makeSPCallCountKey(service_provider_sid);
/* decrement count if INVITE is later rejected */
res.once('end', async({status}) => {
if (status > 200) {
try {
const [calls, callsSP] = await Promise.all([decrKey(keyAccount), decrKey(keySP)]);
logger.info({calls, callsSP}, `decremented call counts after ${status} response`);
} catch (err) {
logger.info({err}, 'Error decrementing call count');
}
nudgeCallCounts(logger, {
service_provider_sid,
account_sid,
application_sid
}, decrKey, {writeCallCountSP, writeCallCount, writeCallCountApp})
.catch((err) => logger.error(err, 'Error decrementing call counts'));
}
});
try {
/* increment the call count */
const [calls, callsSP] = await Promise.all([incrKey(keyAccount), incrKey(keySP)]);
logger.info({calls, callsSP}, 'incremented call counts');
/* write the call counts to the database for both account and service provider */
Promise.all([
writeCallCount(account_sid, calls),
writeCallCountSP(service_provider_sid, callsSP)
]).catch((err) => logger.error({err}, 'Error writing call counts'));
const {callsSP, calls} = await nudgeCallCounts(logger, {
service_provider_sid,
account_sid,
application_sid
}, incrKey, {writeCallCountSP, writeCallCount, writeCallCountApp});
/* compare to account's limit, though avoid db hit when call count is low */
const minLimit = process.env.MIN_CALL_LIMIT ?
@@ -324,7 +323,7 @@ module.exports = function(srf, logger) {
}
}
}
else if (process.env.JAMBONES_TRACK_ACCOUNT_CALLS || process.env.JAMBONES_TRACK_SP_CALLS) {
else if (trackingOn) {
const {account_limit, sp_limit} = await queryCallLimits(service_provider_sid, account_sid);
if (process.env.JAMBONES_TRACK_ACCOUNT_CALLS && account_limit > 0 && calls > account_limit) {
logger.info({calls, account_limit}, 'checkLimits: account limits exceeded');

View File

@@ -43,8 +43,9 @@ const SdpWantsSrtp = (sdp) => {
return /m=audio.*SAVP/.test(sdp);
};
const makeAccountCallCountKey = (sid) => `${sid}:incalls:account`;
const makeSPCallCountKey = (sid) => `${sid}:incalls:sp`;
const makeAccountCallCountKey = (sid) => `incalls:account:${sid}`;
const makeSPCallCountKey = (sid) => `incalls:sp:${sid}:`;
const makeAppCallCountKey = (sid) => `incalls:app${sid}:`;
const normalizeDID = (tel) => {
const regex = /^\+(\d+)$/;
@@ -83,6 +84,77 @@ const createHealthCheckApp = (port, logger) => {
});
};
const nudgeCallCounts = async(logger, sids, nudgeOperator, writers) => {
const {service_provider_sid, account_sid, application_sid} = sids;
const {writeCallCount, writeCallCountSP, writeCallCountApp} = writers;
const nudges = [];
const writes = [];
if (process.env.JAMBONES_TRACK_SP_CALLS) {
const key = makeSPCallCountKey(service_provider_sid);
nudges.push(nudgeOperator(key));
}
else {
nudges.push(() => Promise.resolve(null));
}
if (process.env.JAMBONES_TRACK_ACCOUNT_CALLS || process.env.JAMBONES_HOSTING) {
const key = makeAccountCallCountKey(account_sid);
nudges.push(nudgeOperator(key));
}
else {
nudges.push(() => Promise.resolve(null));
}
if (process.env.JAMBONES_TRACK_APP_CALLS && application_sid) {
const key = makeAppCallCountKey(application_sid);
nudges.push(nudgeOperator(key));
}
else {
nudges.push(() => Promise.resolve(null));
}
try {
const [callsSP, calls, callsApp] = await Promise.all(nudges);
logger.debug({
calls, callsSP, callsApp,
service_provider_sid, account_sid, application_sid}, 'call counts after adjustment');
if (process.env.JAMBONES_TRACK_SP_CALLS) {
writes.push(writeCallCountSP({service_provider_sid, calls_in_progress: callsSP}));
}
if (process.env.JAMBONES_TRACK_ACCOUNT_CALLS || process.env.JAMBONES_HOSTING) {
writes.push(writeCallCount({service_provider_sid, account_sid, calls_in_progress: calls}));
}
if (process.env.JAMBONES_TRACK_APP_CALLS && application_sid) {
writes.push(writeCallCountApp({service_provider_sid, account_sid, application_sid, calls_in_progress: callsApp}));
}
/* write the call counts to the database */
Promise.all(writes).catch((err) => logger.error({err}, 'Error writing call counts'));
return {callsSP, calls, callsApp};
} catch (err) {
logger.error(err, 'error incrementing call counts');
}
return {callsSP: null, calls: null, callsApp: null};
};
const roundTripTime = (startAt) => {
const diff = process.hrtime(startAt);
const time = diff[0] * 1e3 + diff[1] * 1e-6;
return time.toFixed(0);
};
const parseConnectionIp = (sdp) => {
const regex = /c=IN IP4 ([0-9.]+)/;
const arr = regex.exec(sdp);
return arr ? arr[1] : null;
};
module.exports = {
isWSS,
SdpWantsSrtp,
@@ -90,8 +162,12 @@ module.exports = {
makeRtpEngineOpts,
makeAccountCallCountKey,
makeSPCallCountKey,
makeAppCallCountKey,
normalizeDID,
equalsIgnoreOrder,
systemHealth,
createHealthCheckApp
createHealthCheckApp,
nudgeCallCounts,
roundTripTime,
parseConnectionIp
};

118
package-lock.json generated
View File

@@ -13,10 +13,10 @@
"@jambonz/http-authenticator": "^0.2.2",
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/realtimedb-helpers": "^0.4.29",
"@jambonz/rtpengine-utils": "^0.3.1",
"@jambonz/rtpengine-utils": "^0.3.4",
"@jambonz/siprec-client-utils": "^0.1.4",
"@jambonz/stats-collector": "^0.1.6",
"@jambonz/time-series": "^0.2.2",
"@jambonz/time-series": "^0.2.4",
"aws-sdk": "^2.1152.0",
"bent": "^7.3.12",
"cidr-matcher": "^2.1.1",
@@ -632,45 +632,15 @@
}
},
"node_modules/@jambonz/rtpengine-utils": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@jambonz/rtpengine-utils/-/rtpengine-utils-0.3.1.tgz",
"integrity": "sha512-vf0TedWgDhOvlsbGcjrHmSDwyz/85fZ8TjxE50AjD4ueSw/zULwl7wezn3wTz70oTwbzAPYYHnjhyQJns9cwPg==",
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@jambonz/rtpengine-utils/-/rtpengine-utils-0.3.4.tgz",
"integrity": "sha512-7I7Q7O/TuWgk8Z0OX/qGrQZczeDt9pDLnGSwy88NTv5lAbrOYmPyNTC/8VJGfbHISfvBaPYT88+wQn2RIpXRPg==",
"dependencies": {
"debug": "^4.3.1",
"rtpengine-client": "^0.3.2",
"rtpengine-client": "^0.3.6",
"ws": "^8.5.0"
}
},
"node_modules/@jambonz/rtpengine-utils/node_modules/rtpengine-client": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/rtpengine-client/-/rtpengine-client-0.3.2.tgz",
"integrity": "sha512-RbttcmoQxtYU7VCHZR2RHE3eMFShZqq8cMRfPAGw42o4TDdET4Ih4OC0NicVyVRpzlRlzfysTvFWQNXVth03wQ==",
"dependencies": {
"bencode": "^2.0.1",
"uuid": "^8.2.0",
"ws": "^7.4.4"
}
},
"node_modules/@jambonz/rtpengine-utils/node_modules/rtpengine-client/node_modules/ws": {
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz",
"integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/@jambonz/rtpengine-utils/node_modules/ws": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
@@ -710,9 +680,9 @@
}
},
"node_modules/@jambonz/time-series": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.2.tgz",
"integrity": "sha512-QGljKP25R93nDenkuX7bXr7tZfsrRpHTK4ygUWzpRWIe44trY2wUcaTc+HevBSjCSshbOkB/Nn9Tzz19JTzO3w==",
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.4.tgz",
"integrity": "sha512-0rvELi9V/qdyPpM/2LidTd2EDN34iCU6U8ccuLUgqV05FaH/EW0qN0RFaVXVlFK6wCDZqEOBynd7x05De3dncw==",
"dependencies": {
"debug": "^4.3.1",
"influx": "^5.9.3"
@@ -1083,12 +1053,9 @@
]
},
"node_modules/bencode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/bencode/-/bencode-2.0.1.tgz",
"integrity": "sha512-2uhEl8FdjSBUyb69qDTgOEeeqDTa+n3yMQzLW0cOzNf1Ow5bwcg3idf+qsWisIKRH8Bk8oC7UXL8irRcPA8ZEQ==",
"dependencies": {
"safe-buffer": "^5.1.1"
}
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/bencode/-/bencode-2.0.3.tgz",
"integrity": "sha512-D/vrAD4dLVX23NalHwb8dSvsUsxeRPO8Y7ToKA015JQYq69MLDOMkC0uGZYA/MPpltLO8rt8eqFC2j8DxjTZ/w=="
},
"node_modules/bent": {
"version": "7.3.12",
@@ -4371,6 +4338,16 @@
"node": "6.* || >= 7.*"
}
},
"node_modules/rtpengine-client": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/rtpengine-client/-/rtpengine-client-0.3.6.tgz",
"integrity": "sha512-V+o1j0F1bQekFifvkdB9LwdvRX8neMQ6HlG6TG6jFYFCvT1iMrh6BXaKIvn8JvJ4KcwHwTMJGJQSXpLmwa8KsQ==",
"dependencies": {
"bencode": "^2.0.1",
"uuid": "^8.2.0",
"ws": "^7.4.4"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -5805,33 +5782,15 @@
}
},
"@jambonz/rtpengine-utils": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@jambonz/rtpengine-utils/-/rtpengine-utils-0.3.1.tgz",
"integrity": "sha512-vf0TedWgDhOvlsbGcjrHmSDwyz/85fZ8TjxE50AjD4ueSw/zULwl7wezn3wTz70oTwbzAPYYHnjhyQJns9cwPg==",
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@jambonz/rtpengine-utils/-/rtpengine-utils-0.3.4.tgz",
"integrity": "sha512-7I7Q7O/TuWgk8Z0OX/qGrQZczeDt9pDLnGSwy88NTv5lAbrOYmPyNTC/8VJGfbHISfvBaPYT88+wQn2RIpXRPg==",
"requires": {
"debug": "^4.3.1",
"rtpengine-client": "^0.3.2",
"rtpengine-client": "^0.3.6",
"ws": "^8.5.0"
},
"dependencies": {
"rtpengine-client": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/rtpengine-client/-/rtpengine-client-0.3.2.tgz",
"integrity": "sha512-RbttcmoQxtYU7VCHZR2RHE3eMFShZqq8cMRfPAGw42o4TDdET4Ih4OC0NicVyVRpzlRlzfysTvFWQNXVth03wQ==",
"requires": {
"bencode": "^2.0.1",
"uuid": "^8.2.0",
"ws": "^7.4.4"
},
"dependencies": {
"ws": {
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz",
"integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==",
"requires": {}
}
}
},
"ws": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
@@ -5859,9 +5818,9 @@
}
},
"@jambonz/time-series": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.2.tgz",
"integrity": "sha512-QGljKP25R93nDenkuX7bXr7tZfsrRpHTK4ygUWzpRWIe44trY2wUcaTc+HevBSjCSshbOkB/Nn9Tzz19JTzO3w==",
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.4.tgz",
"integrity": "sha512-0rvELi9V/qdyPpM/2LidTd2EDN34iCU6U8ccuLUgqV05FaH/EW0qN0RFaVXVlFK6wCDZqEOBynd7x05De3dncw==",
"requires": {
"debug": "^4.3.1",
"influx": "^5.9.3"
@@ -6154,12 +6113,9 @@
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"bencode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/bencode/-/bencode-2.0.1.tgz",
"integrity": "sha512-2uhEl8FdjSBUyb69qDTgOEeeqDTa+n3yMQzLW0cOzNf1Ow5bwcg3idf+qsWisIKRH8Bk8oC7UXL8irRcPA8ZEQ==",
"requires": {
"safe-buffer": "^5.1.1"
}
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/bencode/-/bencode-2.0.3.tgz",
"integrity": "sha512-D/vrAD4dLVX23NalHwb8dSvsUsxeRPO8Y7ToKA015JQYq69MLDOMkC0uGZYA/MPpltLO8rt8eqFC2j8DxjTZ/w=="
},
"bent": {
"version": "7.3.12",
@@ -8672,6 +8628,16 @@
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
"integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA=="
},
"rtpengine-client": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/rtpengine-client/-/rtpengine-client-0.3.6.tgz",
"integrity": "sha512-V+o1j0F1bQekFifvkdB9LwdvRX8neMQ6HlG6TG6jFYFCvT1iMrh6BXaKIvn8JvJ4KcwHwTMJGJQSXpLmwa8KsQ==",
"requires": {
"bencode": "^2.0.1",
"uuid": "^8.2.0",
"ws": "^7.4.4"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",

View File

@@ -20,7 +20,7 @@
},
"scripts": {
"start": "node app",
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONES_NETWORK_CIDR='127.0.0.1/32' JAMBONES_HOSTING=1 SBC_ACCOUNT_SID=ed649e33-e771-403a-8c99-1780eabbc803 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 JAMBONES_FEATURE_SERVERS=172.38.0.11 node test/ ",
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONES_NETWORK_CIDR='127.0.0.1/32' JAMBONES_HOSTING=1 SBC_ACCOUNT_SID=ed649e33-e771-403a-8c99-1780eabbc803 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=debug DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 JAMBONES_FEATURE_SERVERS=172.38.0.11 node test/ ",
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
"jslint": "eslint app.js lib"
},
@@ -29,10 +29,10 @@
"@jambonz/http-authenticator": "^0.2.2",
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/realtimedb-helpers": "^0.4.29",
"@jambonz/rtpengine-utils": "^0.3.1",
"@jambonz/rtpengine-utils": "^0.3.4",
"@jambonz/siprec-client-utils": "^0.1.4",
"@jambonz/stats-collector": "^0.1.6",
"@jambonz/time-series": "^0.2.2",
"@jambonz/time-series": "^0.2.4",
"aws-sdk": "^2.1152.0",
"bent": "^7.3.12",
"cidr-matcher": "^2.1.1",