mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-19 04:17:44 +00:00
merge features from hosted branch (#32)
major merge of features from the hosted branch that was created temporarily during the initial launch of jambonz.org
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
"jsx": false,
|
||||
"modules": false
|
||||
},
|
||||
"ecmaVersion": 2018
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"plugins": ["promise"],
|
||||
"rules": {
|
||||
|
||||
13
.github/workflows/build.yml
vendored
13
.github/workflows/build.yml
vendored
@@ -1,6 +1,3 @@
|
||||
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
@@ -8,12 +5,18 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
node-version: 14
|
||||
- run: npm ci
|
||||
- run: npm run jslint
|
||||
- run: docker pull drachtio/sipp
|
||||
- run: npm test
|
||||
env:
|
||||
GCP_JSON_KEY: ${{ secrets.GCP_JSON_KEY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_REGION: ${{ secrets.AWS_REGION }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -39,3 +39,5 @@ examples/*
|
||||
|
||||
ecosystem.config.js
|
||||
.vscode
|
||||
test/credentials/*.json
|
||||
run-tests.sh
|
||||
29
app.js
29
app.js
@@ -12,9 +12,10 @@ assert.ok(process.env.JAMBONES_NETWORK_CIDR, 'missing JAMBONES_SUBNET env var');
|
||||
const Srf = require('drachtio-srf');
|
||||
const srf = new Srf();
|
||||
const PORT = process.env.HTTP_PORT || 3000;
|
||||
const opts = Object.assign({
|
||||
timestamp: () => {return `, "time": "${new Date().toISOString()}"`;}
|
||||
}, {level: process.env.JAMBONES_LOGLEVEL || 'info'});
|
||||
const opts = {
|
||||
timestamp: () => {return `, "time": "${new Date().toISOString()}"`;},
|
||||
level: process.env.JAMBONES_LOGLEVEL || 'info'
|
||||
};
|
||||
const logger = require('pino')(opts);
|
||||
const {LifeCycleEvents} = require('./lib/utils/constants');
|
||||
const installSrfLocals = require('./lib/utils/install-srf-locals');
|
||||
@@ -22,6 +23,7 @@ installSrfLocals(srf, logger);
|
||||
|
||||
const {
|
||||
initLocals,
|
||||
getAccountDetails,
|
||||
normalizeNumbers,
|
||||
retrieveApplication,
|
||||
invokeWebCallback
|
||||
@@ -57,7 +59,13 @@ if (process.env.NODE_ENV === 'test') {
|
||||
});
|
||||
}
|
||||
|
||||
srf.use('invite', [initLocals, normalizeNumbers, retrieveApplication, invokeWebCallback]);
|
||||
srf.use('invite', [
|
||||
initLocals,
|
||||
getAccountDetails,
|
||||
normalizeNumbers,
|
||||
retrieveApplication,
|
||||
invokeWebCallback
|
||||
]);
|
||||
|
||||
srf.invite((req, res) => {
|
||||
const session = new InboundCallSession(req, res);
|
||||
@@ -72,7 +80,7 @@ app.use((err, req, res, next) => {
|
||||
logger.error(err, 'burped error');
|
||||
res.status(err.status || 500).json({msg: err.message});
|
||||
});
|
||||
app.listen(PORT);
|
||||
const httpServer = app.listen(PORT);
|
||||
|
||||
logger.info(`listening for HTTP requests on port ${PORT}, serviceUrl is ${srf.locals.serviceUrl}`);
|
||||
|
||||
@@ -88,4 +96,13 @@ setInterval(() => {
|
||||
srf.locals.stats.gauge('fs.sip.calls.count', sessionTracker.count);
|
||||
}, 5000);
|
||||
|
||||
module.exports = {srf, logger};
|
||||
const disconnect = () => {
|
||||
return new Promise ((resolve) => {
|
||||
httpServer.on('close', resolve);
|
||||
httpServer.close();
|
||||
srf.disconnect();
|
||||
srf.locals.mediaservers.forEach((ms) => ms.disconnect());
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {srf, logger, disconnect};
|
||||
|
||||
@@ -3,9 +3,11 @@ const makeTask = require('../../tasks/make_task');
|
||||
const RestCallSession = require('../../session/rest-call-session');
|
||||
const CallInfo = require('../../session/call-info');
|
||||
const {CallDirection, CallStatus} = require('../../utils/constants');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const SipError = require('drachtio-srf').SipError;
|
||||
const sysError = require('./error');
|
||||
const Requestor = require('../../utils/requestor');
|
||||
const dbUtils = require('../../utils/db-utils');
|
||||
|
||||
router.post('/', async(req, res) => {
|
||||
const {logger} = req.app.locals;
|
||||
@@ -15,6 +17,7 @@ router.post('/', async(req, res) => {
|
||||
let uri, cs, to;
|
||||
const restDial = makeTask(logger, {'rest:dial': req.body});
|
||||
const {srf} = require('../../..');
|
||||
const {lookupAccountDetails} = dbUtils(logger, srf);
|
||||
const {getSBC, getFreeswitch} = srf.locals;
|
||||
const sbcAddress = getSBC();
|
||||
if (!sbcAddress) throw new Error('no available SBCs for outbound call creation');
|
||||
@@ -24,13 +27,24 @@ router.post('/', async(req, res) => {
|
||||
headers: req.body.headers || {}
|
||||
};
|
||||
|
||||
|
||||
const {lookupTeamsByAccount, lookupAccountBySid} = srf.locals.dbHelpers;
|
||||
const account = await lookupAccountBySid(req.body.account_sid);
|
||||
const accountInfo = await lookupAccountDetails(req.body.account_sid);
|
||||
const callSid = uuidv4();
|
||||
|
||||
opts.headers = {
|
||||
...opts.headers,
|
||||
'X-Call-Sid': callSid,
|
||||
'X-Account-Sid': req.body.account_sid
|
||||
};
|
||||
|
||||
switch (target.type) {
|
||||
case 'phone':
|
||||
case 'teams':
|
||||
uri = `sip:${target.number}@${sbcAddress}`;
|
||||
to = target.number;
|
||||
if ('teams' === target.type) {
|
||||
const {lookupTeamsByAccount} = srf.locals.dbHelpers;
|
||||
const obj = await lookupTeamsByAccount(req.body.account_sid);
|
||||
if (!obj) throw new Error('dial to ms teams not allowed; account must first be configured with teams info');
|
||||
Object.assign(opts.headers, {
|
||||
@@ -83,8 +97,10 @@ router.post('/', async(req, res) => {
|
||||
* attach our requestor and notifier objects
|
||||
* these will be used for all http requests we make during this call
|
||||
*/
|
||||
app.requestor = new Requestor(logger, app.call_hook);
|
||||
if (app.call_status_hook) app.notifier = new Requestor(logger, app.call_status_hook);
|
||||
app.requestor = new Requestor(logger, account.account_sid, app.call_hook, account.webhook_secret);
|
||||
if (app.call_status_hook) {
|
||||
app.notifier = new Requestor(logger, account.account_sid, app.call_status_hook, account.webhook_secret);
|
||||
}
|
||||
else app.notifier = {request: () => {}};
|
||||
|
||||
/* now launch the outdial */
|
||||
@@ -104,10 +120,11 @@ router.post('/', async(req, res) => {
|
||||
req: inviteReq,
|
||||
to,
|
||||
tag: app.tag,
|
||||
callSid,
|
||||
accountSid: req.body.account_sid,
|
||||
applicationSid: app.application_sid
|
||||
});
|
||||
cs = new RestCallSession({logger, application: app, srf, req: inviteReq, ep, tasks, callInfo});
|
||||
cs = new RestCallSession({logger, application: app, srf, req: inviteReq, ep, tasks, callInfo, accountInfo});
|
||||
cs.exec(req);
|
||||
|
||||
res.status(201).json({sid: cs.callSid});
|
||||
|
||||
@@ -8,12 +8,15 @@ const makeTask = require('../../tasks/make_task');
|
||||
router.post('/:sid', async(req, res) => {
|
||||
const {logger} = req.app.locals;
|
||||
const {srf} = req.app.locals;
|
||||
const {messageSid} = req.body;
|
||||
const {message_sid, account_sid} = req.body;
|
||||
|
||||
logger.debug({body: req.body}, 'got createMessage request');
|
||||
|
||||
const data = [Object.assign({verb: 'message'}, req.body)];
|
||||
delete data[0].messageSid;
|
||||
const data = [{
|
||||
verb: 'message',
|
||||
...req.body
|
||||
}];
|
||||
delete data[0].message_sid;
|
||||
|
||||
try {
|
||||
const tasks = normalizeJambones(logger, data)
|
||||
@@ -21,8 +24,8 @@ router.post('/:sid', async(req, res) => {
|
||||
|
||||
const callInfo = new CallInfo({
|
||||
direction: CallDirection.None,
|
||||
messageSid,
|
||||
accountSid: req.params.sid,
|
||||
messageSid: message_sid,
|
||||
accountSid: account_sid,
|
||||
res
|
||||
});
|
||||
const cs = new SmsSession({logger, srf, tasks, callInfo});
|
||||
|
||||
@@ -13,11 +13,14 @@ router.post('/:partner', async(req, res) => {
|
||||
logger.debug({body: req.body}, `got incomingSms request from partner ${req.params.partner}`);
|
||||
|
||||
let tasks;
|
||||
const {srf} = require('../../..');
|
||||
const {lookupAccountBySid} = srf.locals.dbHelpers;
|
||||
const app = req.body.app;
|
||||
const account = await lookupAccountBySid(app.accountSid);
|
||||
const hook = app.messaging_hook;
|
||||
const requestor = new Requestor(logger, hook);
|
||||
const requestor = new Requestor(logger, account.account_sid, hook, account.webhook_secret);
|
||||
const payload = {
|
||||
provider: req.params.partner,
|
||||
carrier: req.params.partner,
|
||||
messageSid: app.messageSid,
|
||||
accountSid: app.accountSid,
|
||||
applicationSid: app.applicationSid,
|
||||
@@ -38,7 +41,7 @@ router.post('/:partner', async(req, res) => {
|
||||
}
|
||||
|
||||
|
||||
// process any versb in response
|
||||
// process any verbs in response
|
||||
if (Array.isArray(tasks) && tasks.length) {
|
||||
const {srf} = req.app.locals;
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ function retrieveCallSession(callSid, opts) {
|
||||
return cs;
|
||||
}
|
||||
|
||||
const updateCall = async(req, res) => {
|
||||
/**
|
||||
* update a call
|
||||
*/
|
||||
router.post('/:callSid', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
const callSid = req.params.callSid;
|
||||
logger.debug({body: req.body}, 'got upateCall request');
|
||||
@@ -42,23 +45,11 @@ const updateCall = async(req, res) => {
|
||||
logger.info(`updateCall: callSid not found ${callSid}`);
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
res.sendStatus(204);
|
||||
res.sendStatus(202);
|
||||
cs.updateCall(req.body, callSid);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* update a call
|
||||
*/
|
||||
|
||||
/* leaving in for legacy; should have been (and now is) a PUT */
|
||||
router.post('/:callSid', async(req, res) => {
|
||||
await updateCall(req, res);
|
||||
});
|
||||
router.put('/:callSid', async(req, res) => {
|
||||
await updateCall(req, res);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -5,10 +5,11 @@ const Requestor = require('./utils/requestor');
|
||||
const makeTask = require('./tasks/make_task');
|
||||
const parseUri = require('drachtio-srf').parseUri;
|
||||
const normalizeJambones = require('./utils/normalize-jambones');
|
||||
const dbUtils = require('./utils/db-utils');
|
||||
|
||||
module.exports = function(srf, logger) {
|
||||
const {lookupAppByPhoneNumber, lookupAppBySid, lookupAppByRealm, lookupAppByTeamsTenant} = srf.locals.dbHelpers;
|
||||
|
||||
const {lookupAccountDetails} = dbUtils(logger, srf);
|
||||
function initLocals(req, res, next) {
|
||||
const callSid = req.has('X-Retain-Call-Sid') ? req.get('X-Retain-Call-Sid') : uuidv4();
|
||||
req.locals = {
|
||||
@@ -26,6 +27,32 @@ module.exports = function(srf, logger) {
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve account information for the incoming call
|
||||
*/
|
||||
async function getAccountDetails(req, res, next) {
|
||||
|
||||
if (!req.has('X-Account-Sid')) {
|
||||
logger.info('getAccountDetails - rejecting call due to missing X-Account-Sid header');
|
||||
return res.send(500);
|
||||
}
|
||||
const account_sid = req.locals.account_sid = req.get('X-Account-Sid');
|
||||
|
||||
try {
|
||||
req.locals.accountInfo = await lookupAccountDetails(account_sid);
|
||||
if (!req.locals.accountInfo.account.is_active) {
|
||||
logger.info(`Account is inactive or suspended ${account_sid}`);
|
||||
// TODO: alert
|
||||
return res.send(503, {headers: {'X-Reason': 'Account exists but is inactive'}});
|
||||
}
|
||||
logger.debug({accountInfo: req.locals.accountInfo}, `retrieved account info for ${account_sid}`);
|
||||
next();
|
||||
} catch (err) {
|
||||
logger.info({err}, `Error retrieving account details for account ${account_sid}`);
|
||||
res.send(503, {headers: {'X-Reason': `No Account exists for sid ${account_sid}`}});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Within the system, we deal with E.164 numbers _without_ the leading '+
|
||||
*/
|
||||
@@ -52,6 +79,7 @@ module.exports = function(srf, logger) {
|
||||
*/
|
||||
async function retrieveApplication(req, res, next) {
|
||||
const logger = req.locals.logger;
|
||||
const {accountInfo, account_sid} = req.locals;
|
||||
try {
|
||||
let app;
|
||||
if (req.locals.application_sid) app = await lookupAppBySid(req.locals.application_sid);
|
||||
@@ -100,8 +128,9 @@ module.exports = function(srf, logger) {
|
||||
* create a requestor that we will use for all http requests we make during the call.
|
||||
* also create a notifier for call status events (if not needed, its a no-op).
|
||||
*/
|
||||
app.requestor = new Requestor(logger, app.call_hook);
|
||||
if (app.call_status_hook) app.notifier = new Requestor(logger, app.call_status_hook);
|
||||
app.requestor = new Requestor(logger, account_sid, app.call_hook, accountInfo.account.webhook_secret);
|
||||
if (app.call_status_hook) app.notifier = new Requestor(logger, account_sid, app.call_status_hook,
|
||||
accountInfo.account.webhook_secret);
|
||||
else app.notifier = {request: () => {}};
|
||||
|
||||
req.locals.application = app;
|
||||
@@ -145,6 +174,7 @@ module.exports = function(srf, logger) {
|
||||
|
||||
return {
|
||||
initLocals,
|
||||
getAccountDetails,
|
||||
normalizeNumbers,
|
||||
retrieveApplication,
|
||||
invokeWebCallback
|
||||
|
||||
@@ -54,8 +54,8 @@ class CallInfo {
|
||||
}
|
||||
else {
|
||||
// outbound call triggered by REST
|
||||
const {req, accountSid, applicationSid, to, tag} = opts;
|
||||
this.callSid = uuidv4();
|
||||
const {req, callSid, accountSid, applicationSid, to, tag} = opts;
|
||||
this.callSid = callSid;
|
||||
this.accountSid = accountSid;
|
||||
this.applicationSid = applicationSid;
|
||||
this.callStatus = CallStatus.Trying,
|
||||
|
||||
@@ -27,12 +27,13 @@ class CallSession extends Emitter {
|
||||
* @param {array} opts.tasks - tasks we are to execute
|
||||
* @param {callInfo} opts.callInfo - information about the call
|
||||
*/
|
||||
constructor({logger, application, srf, tasks, callInfo}) {
|
||||
constructor({logger, application, srf, tasks, callInfo, accountInfo}) {
|
||||
super();
|
||||
this.logger = logger;
|
||||
this.application = application;
|
||||
this.srf = srf;
|
||||
this.callInfo = callInfo;
|
||||
this.accountInfo = accountInfo;
|
||||
this.tasks = tasks;
|
||||
this.taskIdx = 0;
|
||||
this.stackIdx = 0;
|
||||
@@ -167,6 +168,7 @@ class CallSession extends Emitter {
|
||||
get isAdultingCallSession() {
|
||||
return this.constructor.name === 'AdultingCallSession';
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if this session is a ConfirmCallSession
|
||||
*/
|
||||
@@ -181,6 +183,55 @@ class CallSession extends Emitter {
|
||||
return this.constructor.name === 'SmsCallSession';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for speech credentials for the specified vendor
|
||||
* @param {*} vendor - google or aws
|
||||
*/
|
||||
getSpeechCredentials(vendor, type) {
|
||||
const {writeAlerts, AlertType} = this.srf.locals;
|
||||
this.logger.debug({vendor, type, speech: this.accountInfo.speech}, `searching for speech for vendor ${vendor}`);
|
||||
if (this.accountInfo.speech && this.accountInfo.speech.length > 0) {
|
||||
const credential = this.accountInfo.speech.find((s) => s.vendor === vendor);
|
||||
if (credential && (
|
||||
(type === 'tts' && credential.use_for_tts) ||
|
||||
(type === 'stt' && credential.use_for_stt)
|
||||
)) {
|
||||
if ('google' === vendor) {
|
||||
try {
|
||||
const cred = JSON.parse(credential.service_key.replace(/\n/g, '\\n'));
|
||||
return {
|
||||
speech_credential_sid: credential.speech_credential_sid,
|
||||
credentials: cred
|
||||
};
|
||||
} catch (err) {
|
||||
const sid = this.accountInfo.account.account_sid;
|
||||
this.logger.info({err}, `malformed google service_key provisioned for account ${sid}`);
|
||||
writeAlerts({
|
||||
alert_type: AlertType.TTS_FAILURE,
|
||||
account_sid: this.accountSid,
|
||||
vendor
|
||||
}).catch((err) => this.logger.error({err}, 'Error writing tts alert'));
|
||||
}
|
||||
}
|
||||
else if (['aws', 'polly'].includes(vendor)) {
|
||||
return {
|
||||
speech_credential_sid: credential.speech_credential_sid,
|
||||
accessKeyId: credential.access_key_id,
|
||||
secretAccessKey: credential.secret_access_key,
|
||||
region: process.env.AWS_REGION || credential.aws_region
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
writeAlerts({
|
||||
alert_type: AlertType.STT_NOT_PROVISIONED,
|
||||
account_sid: this.accountSid,
|
||||
vendor
|
||||
}).catch((err) => this.logger.error({err}, 'Error writing tts alert'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the tasks in the CallSession. The tasks are executed in sequence until
|
||||
* they complete, or the caller hangs up.
|
||||
@@ -537,10 +588,10 @@ class CallSession extends Emitter {
|
||||
park_after_bridge: true
|
||||
}).catch((err) => this.logger.error({err}, 'Error setting park_after_bridge'));
|
||||
|
||||
this.logger.debug(`allocated endpoint ${this.ep.uuid}`);
|
||||
this.logger.debug(`allocated endpoint ${ep.uuid}`);
|
||||
|
||||
this.ep.on('destroy', () => {
|
||||
this.logger.info(`endpoint was destroyed!! ${this.ep.uuid}`);
|
||||
this.logger.debug(`endpoint was destroyed!! ${this.ep.uuid}`);
|
||||
});
|
||||
|
||||
if (this.direction === CallDirection.Inbound) {
|
||||
|
||||
@@ -15,6 +15,7 @@ class InboundCallSession extends CallSession {
|
||||
srf: req.srf,
|
||||
application: req.locals.application,
|
||||
callInfo: req.locals.callInfo,
|
||||
accountInfo: req.locals.accountInfo,
|
||||
tasks: req.locals.application.tasks
|
||||
});
|
||||
this.req = req;
|
||||
|
||||
@@ -8,14 +8,15 @@ const moment = require('moment');
|
||||
* @extends CallSession
|
||||
*/
|
||||
class RestCallSession extends CallSession {
|
||||
constructor({logger, application, srf, req, ep, tasks, callInfo}) {
|
||||
constructor({logger, application, srf, req, ep, tasks, callInfo, accountInfo}) {
|
||||
super({
|
||||
logger,
|
||||
application,
|
||||
srf,
|
||||
callSid: callInfo.callSid,
|
||||
tasks,
|
||||
callInfo
|
||||
callInfo,
|
||||
accountInfo
|
||||
});
|
||||
this.req = req;
|
||||
this.ep = ep;
|
||||
|
||||
@@ -135,13 +135,11 @@ class TaskDial extends Task {
|
||||
this.epOther.play(this.dialMusic).catch((err) => {});
|
||||
}
|
||||
}
|
||||
this._installDtmfDetection(cs, this.epOther, this.parentDtmfCollector);
|
||||
if (this.epOther) 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);
|
||||
if (this.epOther) this._removeDtmfDetection(cs, this.epOther);
|
||||
this._removeDtmfDetection(cs, this.ep);
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'TaskDial:exec terminating with error');
|
||||
@@ -151,10 +149,8 @@ class TaskDial extends Task {
|
||||
|
||||
async kill(cs) {
|
||||
super.kill(cs);
|
||||
this._removeDtmfDetection(this.cs, this.epOther);
|
||||
if (this.epOther) 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();
|
||||
@@ -183,7 +179,7 @@ class TaskDial extends Task {
|
||||
await task.exec(cs, callSid === this.callSid ? this.ep : this.epOther);
|
||||
}
|
||||
this.logger.debug('Dial:whisper tasks complete');
|
||||
if (!cs.callGone) this.epOther.bridge(this.ep);
|
||||
if (!cs.callGone && this.epOther) this.epOther.bridge(this.ep);
|
||||
} catch (err) {
|
||||
this.logger.error(err, 'Dial:whisper error');
|
||||
}
|
||||
@@ -221,7 +217,6 @@ class TaskDial extends Task {
|
||||
_removeDtmfDetection(cs, ep) {
|
||||
if (ep) {
|
||||
delete ep.dtmfDetector;
|
||||
this.logger.debug(`Dial:_removeDtmfDetection endpoint ${ep.uuid}`);
|
||||
ep.removeAllListeners('dtmf');
|
||||
}
|
||||
}
|
||||
@@ -238,7 +233,7 @@ class TaskDial extends Task {
|
||||
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))
|
||||
requestor.request(this.dtmfHook, {dtmf: match, ...cs.callInfo})
|
||||
.catch((err) => this.logger.info(err, 'Dial:_onDtmf - error'));
|
||||
}
|
||||
}
|
||||
@@ -250,6 +245,9 @@ class TaskDial extends Task {
|
||||
this.epOther = ep;
|
||||
debug(`Dial:__initializeInbound allocated ep for incoming call: ${ep.uuid}`);
|
||||
|
||||
/* send outbound legs back to the same SBC (to support static IP feature) */
|
||||
if (!this.proxy) this.proxy = `${cs.req.source_address}:${cs.req.source_port};transport=tcp`;
|
||||
|
||||
if (this.dialMusic) {
|
||||
// play dial music to caller while we outdial
|
||||
ep.play(this.dialMusic).catch((err) => {
|
||||
@@ -272,7 +270,10 @@ class TaskDial extends Task {
|
||||
proxy: `sip:${sbcAddress}`,
|
||||
callingNumber: this.callerId || req.callingNumber
|
||||
};
|
||||
Object.assign(opts.headers, {'X-Account-Sid': cs.accountSid});
|
||||
opts.headers = {
|
||||
...opts.headers,
|
||||
'X-Account-Sid': cs.accountSid
|
||||
};
|
||||
|
||||
const t = this.target.find((t) => t.type === 'teams');
|
||||
if (t) {
|
||||
@@ -387,8 +388,10 @@ class TaskDial extends Task {
|
||||
_connectSingleDial(cs, sd) {
|
||||
if (!this.bridged) {
|
||||
this.logger.debug('Dial:_connectSingleDial bridging endpoints');
|
||||
this.epOther.api('uuid_break', this.epOther.uuid);
|
||||
this.epOther.bridge(sd.ep);
|
||||
if (this.epOther) {
|
||||
this.epOther.api('uuid_break', this.epOther.uuid);
|
||||
this.epOther.bridge(sd.ep);
|
||||
}
|
||||
this.bridged = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,8 @@ class Dialogflow extends Task {
|
||||
this.language = cs.speechSynthesisLanguage;
|
||||
this.voice = cs.speechSynthesisVoice;
|
||||
}
|
||||
this.ttsCredentials = cs.getSpeechCredentials(this.vendor, 'tts');
|
||||
|
||||
this.ep.addCustomEventListener('dialogflow::intent', this._onIntent.bind(this, ep, cs));
|
||||
this.ep.addCustomEventListener('dialogflow::transcription', this._onTranscription.bind(this, ep, cs));
|
||||
this.ep.addCustomEventListener('dialogflow::audio_provided', this._onAudioProvided.bind(this, ep, cs));
|
||||
@@ -211,11 +213,13 @@ class Dialogflow extends Task {
|
||||
vendor: this.vendor,
|
||||
language: this.language,
|
||||
voice: this.voice,
|
||||
salt: cs.callSid
|
||||
salt: cs.callSid,
|
||||
credentials: this.ttsCredentials
|
||||
};
|
||||
this.logger.debug({obj}, 'Dialogflow:_onIntent - playing message via tts');
|
||||
const {filePath} = await synthAudio(obj);
|
||||
const {filePath, servedFromCache} = await synthAudio(obj);
|
||||
if (filePath) cs.trackTmpFile(filePath);
|
||||
if (!this.ttsCredentials && !servedFromCache) cs.billForTts(intent.fulfillmentText.length);
|
||||
|
||||
if (this.playInProgress) {
|
||||
await ep.api('uuid_break', ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio'));
|
||||
|
||||
@@ -51,9 +51,23 @@ class TaskGather extends Task {
|
||||
|
||||
async exec(cs, ep) {
|
||||
await super.exec(cs);
|
||||
const {updateSpeechCredentialLastUsed} = require('../utils/db-utils')(this.logger, cs.srf);
|
||||
|
||||
this.ep = ep;
|
||||
if ('default' === this.vendor || !this.vendor) this.vendor = cs.speechRecognizerVendor;
|
||||
if ('default' === this.language || !this.language) this.language = cs.speechRecognizerLanguage;
|
||||
this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt');
|
||||
if (!this.sttCredentials) {
|
||||
const {writeAlerts, AlertType} = cs.srf.locals;
|
||||
this.logger.info(`TaskGather:exec - ERROR stt using ${this.vendor} requested but not creds supplied`);
|
||||
writeAlerts({
|
||||
account_sid: cs.accountSid,
|
||||
alert_type: AlertType.STT_NOT_PROVISIONED,
|
||||
vendor: this.vendor
|
||||
}).catch((err) => this.logger.info({err}, 'Error generating alert for no stt'));
|
||||
|
||||
throw new Error(`no speech-to-text service credentials for ${this.vendor} have been configured`);
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.sayTask) {
|
||||
@@ -71,8 +85,10 @@ class TaskGather extends Task {
|
||||
else this._startTimer();
|
||||
|
||||
if (this.input.includes('speech')) {
|
||||
await this._initSpeech(ep);
|
||||
await this._initSpeech(cs, ep);
|
||||
this._startTranscribing(ep);
|
||||
updateSpeechCredentialLastUsed(this.sttCredentials.speech_credential_sid)
|
||||
.catch(() => {/*already logged error */});
|
||||
}
|
||||
|
||||
if (this.input.includes('digits')) {
|
||||
@@ -105,10 +121,11 @@ class TaskGather extends Task {
|
||||
this._killAudio();
|
||||
}
|
||||
|
||||
async _initSpeech(ep) {
|
||||
async _initSpeech(cs, ep) {
|
||||
const opts = {};
|
||||
|
||||
if ('google' === this.vendor) {
|
||||
if (this.sttCredentials) opts.GOOGLE_APPLICATION_CREDENTIALS = JSON.stringify(this.sttCredentials.credentials);
|
||||
Object.assign(opts, {
|
||||
GOOGLE_SPEECH_USE_ENHANCED: true,
|
||||
GOOGLE_SPEECH_SINGLE_UTTERANCE: true,
|
||||
@@ -121,6 +138,8 @@ class TaskGather extends Task {
|
||||
if (this.profanityFilter === true) {
|
||||
Object.assign(opts, {'GOOGLE_SPEECH_PROFANITY_FILTER': true});
|
||||
}
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance, this._onEndOfUtterance.bind(this, cs, ep));
|
||||
}
|
||||
else {
|
||||
if (this.vocabularyName) opts.AWS_VOCABULARY_NAME = this.vocabularyName;
|
||||
@@ -129,18 +148,16 @@ class TaskGather extends Task {
|
||||
opts.AWS_VOCABULARY_FILTER_METHOD = this.filterMethod || 'mask';
|
||||
}
|
||||
Object.assign(opts, {
|
||||
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
|
||||
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
|
||||
AWS_REGION: process.env.AWS_REGION
|
||||
AWS_ACCESS_KEY_ID: this.sttCredentials.accessKeyId,
|
||||
AWS_SECRET_ACCESS_KEY: this.sttCredentials.secretAccessKey,
|
||||
AWS_REGION: this.sttCredentials.region
|
||||
});
|
||||
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||
}
|
||||
this.logger.debug(`setting freeswitch vars ${JSON.stringify(opts)}`);
|
||||
this.logger.debug({vars: opts}, 'setting freeswitch vars');
|
||||
await ep.set(opts)
|
||||
.catch((err) => this.logger.info(err, 'Error setting channel variables'));
|
||||
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, ep));
|
||||
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, ep));
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance, this._onEndOfUtterance.bind(this, ep));
|
||||
}
|
||||
|
||||
_startTranscribing(ep) {
|
||||
@@ -148,7 +165,16 @@ class TaskGather extends Task {
|
||||
vendor: this.vendor,
|
||||
locale: this.language,
|
||||
interim: this.partialResultCallback ? true : false,
|
||||
}).catch((err) => this.logger.error(err, 'TaskGather:_startTranscribing error'));
|
||||
}).catch((err) => {
|
||||
const {writeAlerts, AlertType} = this.cs.srf.locals;
|
||||
this.logger.error(err, 'TaskGather:_startTranscribing error');
|
||||
writeAlerts({
|
||||
account_sid: this.cs.accountSid,
|
||||
alert_type: AlertType.STT_FAILURE,
|
||||
vendor: this.vendor,
|
||||
detail: err.message
|
||||
});
|
||||
}).catch((err) => this.logger.info({err}, 'Error generating alert for tts failure'));
|
||||
}
|
||||
|
||||
_startTimer() {
|
||||
@@ -175,19 +201,23 @@ class TaskGather extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
_onTranscription(ep, evt) {
|
||||
_onTranscription(cs, ep, evt) {
|
||||
if ('aws' === this.vendor && Array.isArray(evt) && evt.length > 0) evt = evt[0];
|
||||
this.logger.debug(evt, 'TaskGather:_onTranscription');
|
||||
const final = evt.is_final;
|
||||
if (final) this._resolve('speech', evt);
|
||||
if (final) {
|
||||
this._resolve('speech', evt);
|
||||
}
|
||||
else if (this.partialResultHook) {
|
||||
this.cs.requestor.request(this.partialResultHook, Object.assign({speech: evt}, this.cs.callInfo))
|
||||
.catch((err) => this.logger.info(err, 'GatherTask:_onTranscription error'));
|
||||
}
|
||||
}
|
||||
_onEndOfUtterance(ep, evt) {
|
||||
this.logger.info(evt, 'TaskGather:_onEndOfUtterance');
|
||||
this._startTranscribing(ep);
|
||||
_onEndOfUtterance(cs, ep) {
|
||||
this.logger.info('TaskGather:_onEndOfUtterance');
|
||||
if (!this.resolved && !this.killed) {
|
||||
this._startTranscribing(ep);
|
||||
}
|
||||
}
|
||||
|
||||
async _resolve(reason, evt) {
|
||||
@@ -202,13 +232,10 @@ class TaskGather extends Task {
|
||||
|
||||
this._clearTimer();
|
||||
if (reason.startsWith('dtmf')) {
|
||||
await this.performAction({reason: 'dtmfDetected', digits: this.digitBuffer});
|
||||
await this.performAction({digits: this.digitBuffer});
|
||||
}
|
||||
else if (reason.startsWith('speech')) {
|
||||
await this.performAction({reason: 'speechDetected', speech: evt});
|
||||
}
|
||||
else if (reason.startsWith('timeout')) {
|
||||
await this.performAction({reason: 'inputTimeout'});
|
||||
await this.performAction({speech: evt});
|
||||
}
|
||||
this.notifyTaskDone();
|
||||
}
|
||||
|
||||
@@ -103,6 +103,8 @@ class Lex extends Task {
|
||||
this.language = cs.speechSynthesisLanguage;
|
||||
this.voice = cs.speechSynthesisVoice;
|
||||
}
|
||||
this.ttsCredentials = cs.getSpeechCredentials(this.vendor, 'tts');
|
||||
|
||||
this.ep.addCustomEventListener('lex::intent', this._onIntent.bind(this, ep, cs));
|
||||
this.ep.addCustomEventListener('lex::transcription', this._onTranscription.bind(this, ep, cs));
|
||||
this.ep.addCustomEventListener('lex::audio_provided', this._onAudioProvided.bind(this, ep, cs));
|
||||
@@ -184,20 +186,23 @@ class Lex extends Task {
|
||||
|
||||
try {
|
||||
this.logger.debug(`tts with ${this.vendor} ${this.voice}`);
|
||||
const {filepath} = await synthAudio({
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {filePath, servedFromCache} = await synthAudio({
|
||||
text: msg,
|
||||
vendor: this.vendor,
|
||||
language: this.language,
|
||||
voice: this.voice,
|
||||
salt: cs.callSid
|
||||
salt: cs.callSid,
|
||||
credentials: this.ttsCredentials
|
||||
});
|
||||
if (filepath) cs.trackTmpFile(filepath);
|
||||
if (filePath) cs.trackTmpFile(filePath);
|
||||
|
||||
if (this.events.includes('start-play')) {
|
||||
this._performHook(cs, this.eventHook, {event: 'start-play', data: {path: filepath}});
|
||||
this._performHook(cs, this.eventHook, {event: 'start-play', data: {path: filePath}});
|
||||
}
|
||||
await ep.play(filepath);
|
||||
await ep.play(filePath);
|
||||
if (this.events.includes('stop-play')) {
|
||||
this._performHook(cs, this.eventHook, {event: 'stop-play', data: {path: filepath}});
|
||||
this._performHook(cs, this.eventHook, {event: 'stop-play', data: {path: filePath}});
|
||||
}
|
||||
this.logger.debug(`finished tts, sending play_done ${this.vendor} ${this.voice}`);
|
||||
this.ep.api('aws_lex_play_done', this.ep.uuid)
|
||||
|
||||
@@ -8,6 +8,7 @@ class TaskMessage extends Task {
|
||||
this.preconditions = TaskPreconditions.None;
|
||||
|
||||
this.payload = {
|
||||
message_sid: this.data.message_sid,
|
||||
provider: this.data.provider,
|
||||
to: this.data.to,
|
||||
from: this.data.from,
|
||||
@@ -23,21 +24,50 @@ class TaskMessage extends Task {
|
||||
/**
|
||||
* Send outbound SMS
|
||||
*/
|
||||
async exec(cs, dlg) {
|
||||
const {srf} = cs;
|
||||
async exec(cs) {
|
||||
const {srf, accountSid} = cs;
|
||||
const {res} = cs.callInfo;
|
||||
let payload = this.payload;
|
||||
await super.exec(cs);
|
||||
try {
|
||||
const {getSBC} = srf.locals;
|
||||
const sbcAddress = getSBC();
|
||||
if (sbcAddress) {
|
||||
const url = `http://${sbcAddress}:3000/`;
|
||||
const post = bent(url, 'POST', 'json', 200);
|
||||
this.logger.info({payload: this.payload, sbcAddress}, 'Message:exec sending outbound SMS');
|
||||
const response = await post('v1/outboundSMS', this.payload);
|
||||
const {getSBC, getSmpp, dbHelpers} = srf.locals;
|
||||
const {lookupSmppGateways} = dbHelpers;
|
||||
|
||||
this.logger.info(`looking up gateways for account_sid: ${accountSid}`);
|
||||
const r = await lookupSmppGateways(accountSid);
|
||||
let gw, url, relativeUrl;
|
||||
if (r.length > 0) {
|
||||
if (this.payload.provider) gw = r.find((o) => o.vc.name === this.payload.provider);
|
||||
else gw = r[0];
|
||||
}
|
||||
if (gw) {
|
||||
this.logger.info({gw, accountSid}, 'Message:exec - using smpp to send message');
|
||||
url = getSmpp();
|
||||
relativeUrl = '/sms';
|
||||
payload = {
|
||||
...payload,
|
||||
...gw.sg,
|
||||
...gw.vc
|
||||
};
|
||||
}
|
||||
else {
|
||||
this.logger.info({gw, accountSid, provider: this.payload.provider},
|
||||
'Message:exec - no smpp gateways found to send message');
|
||||
relativeUrl = 'v1/outboundSMS';
|
||||
const sbcAddress = getSBC();
|
||||
if (sbcAddress) url = `http://${sbcAddress}:3000/`;
|
||||
|
||||
//TMP: smpp only at the moment, need to add http back in
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
if (url) {
|
||||
const post = bent(url, 'POST', 'json', 201);
|
||||
this.logger.info({payload, url}, 'Message:exec sending outbound SMS');
|
||||
const response = await post(relativeUrl, payload);
|
||||
this.logger.info({response}, 'Successfully sent SMS');
|
||||
if (cs.callInfo.res) {
|
||||
this.logger.info('Message:exec sending 200 OK response to HTTP POST from api server');
|
||||
cs.callInfo.res.status(200).json({
|
||||
res.status(200).json({
|
||||
sid: cs.callInfo.messageSid,
|
||||
providerResponse: response
|
||||
});
|
||||
@@ -46,10 +76,12 @@ class TaskMessage extends Task {
|
||||
// TODO: action Hook
|
||||
}
|
||||
else {
|
||||
this.logger.info('Message:exec - unable to send SMS as there are no available SBCs');
|
||||
this.logger.info('Message:exec - unable to send SMS as there are no available SMS gateways');
|
||||
res.status(422).json({message: 'no configured SMS gateways'});
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.error(err, 'TaskMessage:exec - Error sending SMS');
|
||||
res.status(422).json({message: 'no configured SMS gateways'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,34 +15,64 @@ class TaskSay extends Task {
|
||||
get name() { return TaskName.Say; }
|
||||
|
||||
async exec(cs, ep) {
|
||||
const {srf} = cs;
|
||||
const {synthAudio} = srf.locals.dbHelpers;
|
||||
await super.exec(cs);
|
||||
|
||||
const {srf} = cs;
|
||||
const {updateSpeechCredentialLastUsed} = require('../utils/db-utils')(this.logger, srf);
|
||||
const {writeAlerts, AlertType} = srf.locals;
|
||||
const {synthAudio} = srf.locals.dbHelpers;
|
||||
const vendor = this.synthesizer.vendor || cs.speechSynthesisVendor;
|
||||
const language = this.synthesizer.language || cs.speechSynthesisLanguage;
|
||||
const voice = this.synthesizer.voice || cs.speechSynthesisVoice;
|
||||
const salt = cs.callSid;
|
||||
const credentials = cs.getSpeechCredentials(vendor, 'tts');
|
||||
|
||||
this.ep = ep;
|
||||
try {
|
||||
if (!credentials) {
|
||||
writeAlerts({
|
||||
account_sid: cs.accountSid,
|
||||
alert_type: AlertType.TTS_NOT_PROVISIONED,
|
||||
vendor
|
||||
}).catch((err) => this.logger.info({err}, 'Error generating alert for no tts'));
|
||||
throw new Error('no provisioned speech credentials for TTS');
|
||||
}
|
||||
// synthesize all of the text elements
|
||||
const files = (await Promise.all(this.text.map(async(text) => {
|
||||
const {filePath} = await synthAudio({
|
||||
let lastUpdated = false;
|
||||
const filepath = (await Promise.all(this.text.map(async(text) => {
|
||||
const {filePath, servedFromCache} = await synthAudio({
|
||||
text,
|
||||
vendor: this.synthesizer.vendor || cs.speechSynthesisVendor,
|
||||
language: this.synthesizer.language || cs.speechSynthesisLanguage,
|
||||
voice: this.synthesizer.voice || cs.speechSynthesisVoice,
|
||||
salt: cs.callSid
|
||||
}).catch((err) => this.logger.error(err, 'Error synthesizing text'));
|
||||
vendor,
|
||||
language,
|
||||
voice,
|
||||
salt,
|
||||
credentials
|
||||
}).catch((err) => {
|
||||
this.logger.info(err, 'Error synthesizing tts');
|
||||
writeAlerts({
|
||||
account_sid: cs.accountSid,
|
||||
alert_type: AlertType.TTS_NOT_PROVISIONED,
|
||||
vendor,
|
||||
detail: err.message
|
||||
});
|
||||
}).catch((err) => this.logger.info({err}, 'Error generating alert for tts failure'));
|
||||
this.logger.debug(`file ${filePath}, served from cache ${servedFromCache}`);
|
||||
if (filePath) cs.trackTmpFile(filePath);
|
||||
if (!servedFromCache && !lastUpdated) {
|
||||
lastUpdated = true;
|
||||
updateSpeechCredentialLastUsed(credentials.speech_credential_sid)
|
||||
.catch(() => {/*already logged error */});
|
||||
}
|
||||
return filePath;
|
||||
})))
|
||||
.filter((fp) => fp && fp.length);
|
||||
}))).filter((fp) => fp && fp.length);
|
||||
|
||||
this.logger.debug({files, loop: this.loop}, 'synthesized files for tts');
|
||||
if (!this.ep.connected) this.logger.debug('say: endpoint is not connected!');
|
||||
this.logger.debug({filepath}, 'synthesized files for tts');
|
||||
|
||||
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);
|
||||
await ep.play(filepath[segment]);
|
||||
} while (!this.killed && ++segment < filepath.length);
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.info(err, 'TaskSay:exec error');
|
||||
|
||||
@@ -166,10 +166,6 @@
|
||||
"passDtmf": "boolean",
|
||||
"actionHook": "object|string",
|
||||
"eventHook": "object|string",
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"enum": ["lex", "tts"]
|
||||
},
|
||||
"noInputTimeout": "number",
|
||||
"tts": "#synthesizer"
|
||||
},
|
||||
@@ -177,7 +173,7 @@
|
||||
"botId",
|
||||
"botAlias",
|
||||
"region",
|
||||
"prompt"
|
||||
"credentials"
|
||||
]
|
||||
},
|
||||
"listen": {
|
||||
@@ -206,7 +202,9 @@
|
||||
},
|
||||
"message": {
|
||||
"properties": {
|
||||
"provider": "string",
|
||||
"carrier": "string",
|
||||
"account_sid": "string",
|
||||
"message_sid": "string",
|
||||
"to": "string",
|
||||
"from": "string",
|
||||
"text": "string",
|
||||
@@ -292,8 +290,7 @@
|
||||
"sipUri": "string",
|
||||
"auth": "#auth",
|
||||
"vmail": "boolean",
|
||||
"tenant": "string",
|
||||
"overrideTo": "string"
|
||||
"tenant": "string"
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
|
||||
@@ -200,6 +200,7 @@ class Task extends Emitter {
|
||||
}
|
||||
if (required.length > 0) throw new Error(`${name}: missing value for ${required}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Task;
|
||||
|
||||
@@ -44,11 +44,22 @@ class TaskTranscribe extends Task {
|
||||
|
||||
async exec(cs, ep, parentTask) {
|
||||
super.exec(cs);
|
||||
const {updateSpeechCredentialLastUsed} = require('../utils/db-utils')(this.logger, cs.srf);
|
||||
|
||||
this.ep = ep;
|
||||
if ('default' === this.vendor || !this.vendor) this.vendor = cs.speechRecognizerVendor;
|
||||
if ('default' === this.language || !this.language) this.language = cs.speechRecognizerLanguage;
|
||||
this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt');
|
||||
|
||||
try {
|
||||
await this._startTranscribing(ep);
|
||||
if (!this.sttCredentials) {
|
||||
// TODO: generate alert (actually should be done by cs.getSpeechCredentials)
|
||||
throw new Error('no provisioned speech credentials for TTS');
|
||||
}
|
||||
await this._startTranscribing(cs, ep);
|
||||
updateSpeechCredentialLastUsed(this.sttCredentials.speech_credential_sid)
|
||||
.catch(() => {/*already logged error */});
|
||||
|
||||
await this.awaitTaskDone();
|
||||
} catch (err) {
|
||||
this.logger.info(err, 'TaskTranscribe:exec - error');
|
||||
@@ -74,19 +85,20 @@ class TaskTranscribe extends Task {
|
||||
await this.awaitTaskDone();
|
||||
}
|
||||
|
||||
async _startTranscribing(ep) {
|
||||
async _startTranscribing(cs, ep) {
|
||||
const opts = {};
|
||||
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, ep));
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, ep));
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, cs, ep));
|
||||
ep.addCustomEventListener(GoogleTranscriptionEvents.MaxDurationExceeded,
|
||||
this._onMaxDurationExceeded.bind(this, ep));
|
||||
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, ep));
|
||||
ep.addCustomEventListener(AwsTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, ep));
|
||||
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||
ep.addCustomEventListener(AwsTranscriptionEvents.NoAudioDetected, this._onNoAudio.bind(this, cs, ep));
|
||||
ep.addCustomEventListener(AwsTranscriptionEvents.MaxDurationExceeded,
|
||||
this._onMaxDurationExceeded.bind(this, ep));
|
||||
this._onMaxDurationExceeded.bind(this, cs, ep));
|
||||
|
||||
if (this.vendor === 'google') {
|
||||
if (this.sttCredentials) opts.GOOGLE_APPLICATION_CREDENTIALS = JSON.stringify(this.sttCredentials.credentials);
|
||||
[
|
||||
['enhancedModel', 'GOOGLE_SPEECH_USE_ENHANCED'],
|
||||
['separateRecognitionPerChannel', 'GOOGLE_SPEECH_SEPARATE_RECOGNITION_PER_CHANNEL'],
|
||||
@@ -134,11 +146,20 @@ class TaskTranscribe extends Task {
|
||||
opts.AWS_VOCABULARY_FILTER_METHOD = this.filterMethod || 'mask';
|
||||
}
|
||||
|
||||
Object.assign(opts, {
|
||||
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
|
||||
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
|
||||
AWS_REGION: process.env.AWS_REGION
|
||||
});
|
||||
if (this.sttCredentials) {
|
||||
Object.assign(opts, {
|
||||
AWS_ACCESS_KEY_ID: this.sttCredentials.accessKeyId,
|
||||
AWS_SECRET_ACCESS_KEY: this.sttCredentials.secretAccessKey,
|
||||
AWS_REGION: this.sttCredentials.region
|
||||
});
|
||||
}
|
||||
else {
|
||||
Object.assign(opts, {
|
||||
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
|
||||
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
|
||||
AWS_REGION: process.env.AWS_REGION
|
||||
});
|
||||
}
|
||||
|
||||
await ep.set(opts)
|
||||
.catch((err) => this.logger.info(err, 'TaskTranscribe:_startTranscribing with aws'));
|
||||
@@ -155,8 +176,10 @@ class TaskTranscribe extends Task {
|
||||
});
|
||||
}
|
||||
|
||||
_onTranscription(ep, evt) {
|
||||
_onTranscription(cs, ep, evt) {
|
||||
if ('aws' === this.vendor && Array.isArray(evt) && evt.length > 0) evt = evt[0];
|
||||
this.logger.debug(evt, 'TaskTranscribe:_onTranscription');
|
||||
|
||||
this.cs.requestor.request(this.transcriptionHook, Object.assign({speech: evt}, this.cs.callInfo))
|
||||
.catch((err) => this.logger.info(err, 'TranscribeTask:_onTranscription error'));
|
||||
if (this.killed) {
|
||||
@@ -166,12 +189,12 @@ class TaskTranscribe extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
_onNoAudio(ep) {
|
||||
_onNoAudio(cs, ep) {
|
||||
this.logger.debug('TaskTranscribe:_onNoAudio restarting transcription');
|
||||
this._transcribe(ep);
|
||||
}
|
||||
|
||||
_onMaxDurationExceeded(ep) {
|
||||
_onMaxDurationExceeded(cs, ep) {
|
||||
this.logger.debug('TaskTranscribe:_onMaxDurationExceeded restarting transcription');
|
||||
this._transcribe(ep);
|
||||
}
|
||||
|
||||
74
lib/utils/db-utils.js
Normal file
74
lib/utils/db-utils.js
Normal file
@@ -0,0 +1,74 @@
|
||||
const {decrypt} = require('./encrypt-decrypt');
|
||||
|
||||
const sqlAccountDetails = `SELECT *
|
||||
FROM accounts account
|
||||
WHERE account.account_sid = ?`;
|
||||
const sqlSpeechCredentials = `SELECT *
|
||||
FROM speech_credentials
|
||||
WHERE account_sid = ? `;
|
||||
const sqlSpeechCredentialsForSP = `SELECT *
|
||||
FROM speech_credentials
|
||||
WHERE service_provider_sid =
|
||||
(SELECT service_provider_sid from accounts where account_sid = ?)`;
|
||||
|
||||
const speechMapper = (cred) => {
|
||||
const {credential, ...obj} = cred;
|
||||
if ('google' === obj.vendor) {
|
||||
obj.service_key = decrypt(credential);
|
||||
}
|
||||
else if ('aws' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.access_key_id = o.access_key_id;
|
||||
obj.secret_access_key = o.secret_access_key;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
module.exports = (logger, srf) => {
|
||||
const {pool} = srf.locals.dbHelpers;
|
||||
const pp = pool.promise();
|
||||
|
||||
const lookupAccountDetails = async(account_sid) => {
|
||||
const [r] = await pp.query({sql: sqlAccountDetails, nestTables: true}, account_sid);
|
||||
if (0 === r.length) throw new Error(`invalid accountSid: ${account_sid}`);
|
||||
const [r2] = await pp.query(sqlSpeechCredentials, account_sid);
|
||||
const speech = r2.map(speechMapper);
|
||||
|
||||
/* search at the service provider level if we don't find it at the account level */
|
||||
const haveGoogle = speech.find((s) => s.vendor === 'google');
|
||||
const haveAws = speech.find((s) => s.vendor === 'aws');
|
||||
if (!haveGoogle || !haveAws) {
|
||||
const [r3] = await pp.query(sqlSpeechCredentialsForSP, account_sid);
|
||||
if (r3.length) {
|
||||
if (!haveGoogle) {
|
||||
const google = r3.find((s) => s.vendor === 'google');
|
||||
if (google) speech.push(speechMapper(google));
|
||||
}
|
||||
if (!haveAws) {
|
||||
const aws = r3.find((s) => s.vendor === 'aws');
|
||||
if (aws) speech.push(speechMapper(aws));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...r[0],
|
||||
speech
|
||||
};
|
||||
};
|
||||
|
||||
const updateSpeechCredentialLastUsed = async(speech_credential_sid) => {
|
||||
const pp = pool.promise();
|
||||
const sql = 'UPDATE speech_credentials SET last_used = NOW() WHERE speech_credential_sid = ?';
|
||||
try {
|
||||
await pp.execute(sql, [speech_credential_sid]);
|
||||
} catch (err) {
|
||||
logger.error({err}, `Error updating last_used for speech_credential_sid ${speech_credential_sid}`);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
lookupAccountDetails,
|
||||
updateSpeechCredentialLastUsed
|
||||
};
|
||||
};
|
||||
35
lib/utils/encrypt-decrypt.js
Normal file
35
lib/utils/encrypt-decrypt.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const crypto = require('crypto');
|
||||
const algorithm = 'aes-256-ctr';
|
||||
const iv = crypto.randomBytes(16);
|
||||
const secretKey = crypto.createHash('sha256')
|
||||
.update(String(process.env.JWT_SECRET))
|
||||
.digest('base64')
|
||||
.substr(0, 32);
|
||||
|
||||
const encrypt = (text) => {
|
||||
const cipher = crypto.createCipheriv(algorithm, secretKey, iv);
|
||||
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
|
||||
const data = {
|
||||
iv: iv.toString('hex'),
|
||||
content: encrypted.toString('hex')
|
||||
};
|
||||
return JSON.stringify(data);
|
||||
};
|
||||
|
||||
const decrypt = (data) => {
|
||||
let hash;
|
||||
try {
|
||||
hash = JSON.parse(data);
|
||||
} catch (err) {
|
||||
console.log(`failed to parse json string ${data}`);
|
||||
throw err;
|
||||
}
|
||||
const decipher = crypto.createDecipheriv(algorithm, secretKey, Buffer.from(hash.iv, 'hex'));
|
||||
const decrpyted = Buffer.concat([decipher.update(Buffer.from(hash.content, 'hex')), decipher.final()]);
|
||||
return decrpyted.toString();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
encrypt,
|
||||
decrypt
|
||||
};
|
||||
@@ -38,9 +38,12 @@ function installSrfLocals(srf, logger) {
|
||||
const fsInventory = process.env.JAMBONES_FREESWITCH
|
||||
.split(',')
|
||||
.map((fs) => {
|
||||
const arr = /^(.*):(.*):(.*)/.exec(fs);
|
||||
const arr = /^([^:]*):([^:]*):([^:]*)(?::([^:]*))?/.exec(fs);
|
||||
assert.ok(arr, `Invalid syntax JAMBONES_FREESWITCH: ${process.env.JAMBONES_FREESWITCH}`);
|
||||
return {address: arr[1], port: arr[2], secret: arr[3]};
|
||||
const opts = {address: arr[1], port: arr[2], secret: arr[3]};
|
||||
if (arr.length > 4) opts.advertisedAddress = arr[4];
|
||||
if (process.env.NODE_ENV === 'test') opts.listenAddress = '0.0.0.0';
|
||||
return opts;
|
||||
});
|
||||
logger.info({fsInventory}, 'freeswitch inventory');
|
||||
|
||||
@@ -72,6 +75,7 @@ function installSrfLocals(srf, logger) {
|
||||
|
||||
// if we have a single freeswitch (as is typical) report stats periodically
|
||||
if (mediaservers.length === 1) {
|
||||
srf.locals.mediaservers = [mediaservers[0].ms];
|
||||
setInterval(() => {
|
||||
try {
|
||||
if (mediaservers[0].ms && mediaservers[0].active) {
|
||||
@@ -99,20 +103,25 @@ function installSrfLocals(srf, logger) {
|
||||
}
|
||||
|
||||
const {
|
||||
pool,
|
||||
lookupAppByPhoneNumber,
|
||||
lookupAppBySid,
|
||||
lookupAppByRealm,
|
||||
lookupAppByTeamsTenant,
|
||||
lookupTeamsByAccount,
|
||||
lookupAccountBySid
|
||||
lookupAccountBySid,
|
||||
lookupAccountCapacitiesBySid,
|
||||
lookupSmppGateways
|
||||
} = require('@jambonz/db-helpers')({
|
||||
host: process.env.JAMBONES_MYSQL_HOST,
|
||||
user: process.env.JAMBONES_MYSQL_USER,
|
||||
port: process.env.JAMBONES_MYSQL_PORT || 3306,
|
||||
password: process.env.JAMBONES_MYSQL_PASSWORD,
|
||||
database: process.env.JAMBONES_MYSQL_DATABASE,
|
||||
connectionLimit: process.env.JAMBONES_MYSQL_CONNECTION_LIMIT || 10
|
||||
}, logger);
|
||||
const {
|
||||
client,
|
||||
updateCallStatus,
|
||||
retrieveCall,
|
||||
listCalls,
|
||||
@@ -135,15 +144,27 @@ function installSrfLocals(srf, logger) {
|
||||
host: process.env.JAMBONES_REDIS_HOST,
|
||||
port: process.env.JAMBONES_REDIS_PORT || 6379
|
||||
}, logger);
|
||||
const {
|
||||
writeAlerts,
|
||||
AlertType
|
||||
} = require('@jambonz/time-series')(logger, {
|
||||
host: process.env.JAMBONES_TIME_SERIES_HOST,
|
||||
commitSize: 50,
|
||||
commitInterval: 'test' === process.env.NODE_ENV ? 7 : 20
|
||||
});
|
||||
|
||||
Object.assign(srf.locals, {
|
||||
srf.locals = {...srf.locals,
|
||||
dbHelpers: {
|
||||
client,
|
||||
pool,
|
||||
lookupAppByPhoneNumber,
|
||||
lookupAppBySid,
|
||||
lookupAppByRealm,
|
||||
lookupAppByTeamsTenant,
|
||||
lookupTeamsByAccount,
|
||||
lookupAccountBySid,
|
||||
lookupAccountCapacitiesBySid,
|
||||
lookupSmppGateways,
|
||||
updateCallStatus,
|
||||
retrieveCall,
|
||||
listCalls,
|
||||
@@ -167,10 +188,15 @@ function installSrfLocals(srf, logger) {
|
||||
ipv4: localIp,
|
||||
serviceUrl: `http://${localIp}:${PORT}`,
|
||||
getSBC,
|
||||
getSmpp: () => {
|
||||
return process.env.SMPP_URL;
|
||||
},
|
||||
lifecycleEmitter,
|
||||
getFreeswitch,
|
||||
stats: stats
|
||||
});
|
||||
stats: stats,
|
||||
writeAlerts,
|
||||
AlertType
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = installSrfLocals;
|
||||
|
||||
@@ -61,6 +61,7 @@ class SingleDialer extends Emitter {
|
||||
async exec(srf, ms, opts) {
|
||||
opts = opts || {};
|
||||
opts.headers = opts.headers || {};
|
||||
opts.headers = {...opts.headers, 'X-Call-Sid': this.callSid};
|
||||
let uri, to;
|
||||
try {
|
||||
switch (this.target.type) {
|
||||
@@ -71,10 +72,10 @@ class SingleDialer extends Emitter {
|
||||
to = this.target.number;
|
||||
if ('teams' === this.target.type) {
|
||||
assert(this.target.teamsInfo);
|
||||
Object.assign(opts.headers, {
|
||||
opts.headers = {...opts.headers,
|
||||
'X-MS-Teams-FQDN': this.target.teamsInfo.ms_teams_fqdn,
|
||||
'X-MS-Teams-Tenant-FQDN': this.target.teamsInfo.tenant_fqdn
|
||||
});
|
||||
};
|
||||
if (this.target.vmail === true) uri = `${uri};opaque=app:voicemail`;
|
||||
}
|
||||
break;
|
||||
@@ -84,12 +85,6 @@ class SingleDialer extends Emitter {
|
||||
uri = `sip:${this.target.name}`;
|
||||
to = this.target.name;
|
||||
|
||||
if (this.target.overrideTo) {
|
||||
Object.assign(opts.headers, {
|
||||
'X-Override-To': this.target.overrideTo
|
||||
});
|
||||
}
|
||||
|
||||
// need to send to the SBC registered on
|
||||
const reg = await registrar.query(aor);
|
||||
if (reg) {
|
||||
@@ -267,7 +262,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.baseLogger,
|
||||
logger: this.logger,
|
||||
application: this.application,
|
||||
dlg: this.dlg,
|
||||
ep: this.ep,
|
||||
|
||||
@@ -2,8 +2,30 @@ const bent = require('bent');
|
||||
const parseUrl = require('parse-url');
|
||||
const assert = require('assert');
|
||||
const snakeCaseKeys = require('./snakecase-keys');
|
||||
const crypto = require('crypto');
|
||||
const timeSeries = require('@jambonz/time-series');
|
||||
let alerter ;
|
||||
|
||||
const toBase64 = (str) => Buffer.from(str || '', 'utf8').toString('base64');
|
||||
|
||||
function computeSignature(payload, timestamp, secret) {
|
||||
assert(secret);
|
||||
const data = `${timestamp}.${JSON.stringify(payload)}`;
|
||||
return crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(data, 'utf8')
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
function generateSigHeader(payload, secret) {
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
const signature = computeSignature(payload, timestamp, secret);
|
||||
const scheme = 'v1';
|
||||
return {
|
||||
'Jambonz-Signature': `t=${timestamp},${scheme}=${signature}`
|
||||
};
|
||||
}
|
||||
|
||||
function basicAuth(username, password) {
|
||||
if (!username || !password) return {};
|
||||
const creds = `${username}:${password || ''}`;
|
||||
@@ -21,7 +43,7 @@ function isAbsoluteUrl(u) {
|
||||
}
|
||||
|
||||
class Requestor {
|
||||
constructor(logger, hook) {
|
||||
constructor(logger, account_sid, hook, secret) {
|
||||
assert(typeof hook === 'object');
|
||||
|
||||
this.logger = logger;
|
||||
@@ -38,12 +60,22 @@ class Requestor {
|
||||
|
||||
this.username = hook.username;
|
||||
this.password = hook.password;
|
||||
this.secret = secret;
|
||||
this.account_sid = account_sid;
|
||||
|
||||
assert(isAbsoluteUrl(this.url));
|
||||
assert(['GET', 'POST'].includes(this.method));
|
||||
|
||||
const {stats} = require('../../').srf.locals;
|
||||
this.stats = stats;
|
||||
|
||||
if (!alerter) {
|
||||
alerter = timeSeries(logger, {
|
||||
host: process.env.JAMBONES_TIME_SERIES_HOST,
|
||||
commitSize: 50,
|
||||
commitInterval: 'test' === process.env.NODE_ENV ? 7 : 20
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get baseUrl() {
|
||||
@@ -65,7 +97,6 @@ class Requestor {
|
||||
const payload = params ? snakeCaseKeys(params, ['customerData', 'sip']) : null;
|
||||
const url = hook.url || hook;
|
||||
const method = hook.method || 'POST';
|
||||
const {username, password} = typeof hook === 'object' ? hook : {};
|
||||
|
||||
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}`);
|
||||
@@ -75,12 +106,27 @@ class Requestor {
|
||||
|
||||
let buf;
|
||||
try {
|
||||
const sigHeader = generateSigHeader(payload, this.secret);
|
||||
const headers = {...sigHeader, ...this.authHeader};
|
||||
this.logger.info({url, headers}, 'send webhook');
|
||||
buf = isRelativeUrl(url) ?
|
||||
await this.post(url, payload, this.authHeader) :
|
||||
await bent(method, 'buffer', 200, 201, 202)(url, payload, basicAuth(username, password));
|
||||
await this.post(url, payload, headers) :
|
||||
await bent(method, 'buffer', 200, 201, 202)(url, payload, headers);
|
||||
} catch (err) {
|
||||
this.logger.info({baseUrl: this.baseUrl, url, statusCode: err.statusCode},
|
||||
this.logger.error({err, secret: this.secret, baseUrl: this.baseUrl, url, statusCode: err.statusCode},
|
||||
`web callback returned unexpected error code ${err.statusCode}`);
|
||||
let opts = {account_sid: this.account_sid};
|
||||
if (err.code === 'ECONNREFUSED') {
|
||||
opts = {...opts, alert_type: alerter.AlertType.WEBHOOK_CONNECTION_FAILURE, url};
|
||||
}
|
||||
else if (err.name === 'StatusError') {
|
||||
opts = {...opts, alert_type: alerter.AlertType.WEBHOOK_STATUS_FAILURE, url, status: err.statusCode};
|
||||
}
|
||||
else {
|
||||
opts = {...opts, alert_type: alerter.AlertType.WEBHOOK_CONNECTION_FAILURE, url, detail: err.message};
|
||||
}
|
||||
alerter.writeAlerts(opts).catch((err) => this.logger.info({err, opts}, 'Error writing alert'));
|
||||
|
||||
throw err;
|
||||
}
|
||||
const diff = process.hrtime(startAt);
|
||||
|
||||
@@ -68,6 +68,8 @@ module.exports = (logger) => {
|
||||
|
||||
// send OPTIONS pings to SBCs
|
||||
async function pingProxies(srf) {
|
||||
if (process.env.NODE_ENV === 'test') return;
|
||||
|
||||
for (const sbc of sbcs) {
|
||||
try {
|
||||
const ms = srf.locals.getFreeswitch();
|
||||
|
||||
865
package-lock.json
generated
865
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@@ -21,17 +21,17 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app",
|
||||
"test": "NODE_ENV=test JAMBONES_NETWORK_CIDR=127.0.0.1/32 node test/ | ./node_modules/.bin/tap-spec",
|
||||
"test": "NODE_ENV=test JAMBONES_HOSTING=1 DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 DRACHTIO_SECRET=cymru JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_PORT=3360 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=127.0.0.1 JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=debug ENABLE_METRICS=0 HTTP_PORT=3000 JAMBONES_SBCS=172.38.0.10 JAMBONES_FREESWITCH=127.0.0.1:8022:ClueCon:docker-host JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_NETWORK_CIDR=172.38.0.0/16 node test/ ",
|
||||
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
|
||||
"jslint": "eslint app.js lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jambonz/db-helpers": "^0.5.20",
|
||||
"@jambonz/mw-registrar": "^0.1.9",
|
||||
"@jambonz/db-helpers": "^0.6.4",
|
||||
"@jambonz/mw-registrar": "^0.2.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.1",
|
||||
"@jambonz/stats-collector": "^0.1.5",
|
||||
"@jambonz/time-series": "^0.1.5",
|
||||
"aws-sdk": "^2.848.0",
|
||||
"aws-sdk": "^2.846.0",
|
||||
"bent": "^7.3.12",
|
||||
"cidr-matcher": "^2.1.1",
|
||||
"debug": "^4.3.1",
|
||||
@@ -42,7 +42,7 @@
|
||||
"ip": "^1.1.5",
|
||||
"moment": "^2.29.1",
|
||||
"parse-url": "^5.0.2",
|
||||
"pino": "^6.11.1",
|
||||
"pino": "^6.11.2",
|
||||
"to-snake-case": "^1.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"verify-aws-sns-signature": "^0.0.6",
|
||||
@@ -50,12 +50,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"async": "^3.2.0",
|
||||
"blue-tape": "^1.0.0",
|
||||
"clear-module": "^4.1.1",
|
||||
"eslint": "^7.20.0",
|
||||
"eslint-plugin-promise": "^4.3.1",
|
||||
"nyc": "^15.1.0",
|
||||
"tap-spec": "^5.0.0",
|
||||
"tape": "^5.2.0"
|
||||
"tape": "^5.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
34
test/account-validation-tests.js
Normal file
34
test/account-validation-tests.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const test = require('tape');
|
||||
const { sippUac } = require('./sipp')('test_fs');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
function connect(connectable) {
|
||||
return new Promise((resolve, reject) => {
|
||||
connectable.on('connect', () => {
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('account validation tests', async(t) => {
|
||||
const {srf, disconnect} = require('../app');
|
||||
|
||||
try {
|
||||
await connect(srf);
|
||||
await sippUac('uac-expect-500.xml', '172.38.0.10');
|
||||
t.pass('rejected INVITE without X-Account-Sid header');
|
||||
await sippUac('uac-invalid-account-expect-503.xml', '172.38.0.10');
|
||||
t.pass('rejected INVITE with invalid X-Account-Sid header');
|
||||
await sippUac('uac-inactive-account-expect-503.xml', '172.38.0.10');
|
||||
t.pass('rejected INVITE from inactive account');
|
||||
|
||||
disconnect();
|
||||
} catch (err) {
|
||||
console.log(`error received: ${err}`);
|
||||
disconnect();
|
||||
t.error(err);
|
||||
}
|
||||
});
|
||||
@@ -1,9 +1,12 @@
|
||||
const test = require('tape').test ;
|
||||
const test = require('tape') ;
|
||||
const exec = require('child_process').exec ;
|
||||
const pwd = process.env.TRAVIS ? '' : '-p$MYSQL_ROOT_PASSWORD';
|
||||
const fs = require('fs');
|
||||
const {encrypt} = require('../lib/utils/encrypt-decrypt');
|
||||
|
||||
test('creating jambones_test database', (t) => {
|
||||
exec(`mysql -h localhost -u root ${pwd} < ${__dirname}/db/create_test_db.sql`, (err, stdout, stderr) => {
|
||||
exec(`mysql -h 127.0.0.1 -u root --protocol=tcp --port=3360 < ${__dirname}/db/create_test_db.sql`, (err, stdout, stderr) => {
|
||||
console.log(stdout);
|
||||
console.log(stderr)
|
||||
if (err) return t.end(err);
|
||||
t.pass('database successfully created');
|
||||
t.end();
|
||||
@@ -11,17 +14,35 @@ test('creating jambones_test database', (t) => {
|
||||
});
|
||||
|
||||
test('creating schema', (t) => {
|
||||
exec(`mysql -h localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/jambones-sql.sql`, (err, stdout, stderr) => {
|
||||
exec(`mysql -h 127.0.0.1 -u root --protocol=tcp --port=3360 -D jambones_test < ${__dirname}/db/create-and-populate-schema.sql`, (err, stdout, stderr) => {
|
||||
if (err) return t.end(err);
|
||||
t.pass('schema successfully created');
|
||||
t.end();
|
||||
t.pass('schema and test data successfully created');
|
||||
|
||||
if (process.env.GCP_JSON_KEY && process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
|
||||
const google_credential = encrypt(process.env.GCP_JSON_KEY);
|
||||
const aws_credential = encrypt(JSON.stringify({
|
||||
access_key_id: process.env.AWS_ACCESS_KEY_ID,
|
||||
secret_access_key: process.env.AWS_SECRET_ACCESS_KEY
|
||||
}));
|
||||
const cmd = `
|
||||
UPDATE speech_credentials SET credential='${google_credential}' WHERE vendor='google';
|
||||
UPDATE speech_credentials SET credential='${aws_credential}' WHERE vendor='aws';
|
||||
`;
|
||||
const path = `${__dirname}/.creds.sql`;
|
||||
fs.writeFileSync(path, cmd);
|
||||
exec(`mysql -h 127.0.0.1 -u root --protocol=tcp --port=3360 -D jambones_test < ${path}`, (err, stdout, stderr) => {
|
||||
console.log(stdout);
|
||||
console.log(stderr);
|
||||
if (err) return t.end(err);
|
||||
fs.unlinkSync(path)
|
||||
fs.writeFileSync(`${__dirname}/credentials/gcp.json`, process.env.GCP_JSON_KEY);
|
||||
t.pass('set account-level speech credentials');
|
||||
t.end();
|
||||
});
|
||||
}
|
||||
else {
|
||||
t.end();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('populating test case data', (t) => {
|
||||
exec(`mysql -h localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/populate-test-data.sql`, (err, stdout, stderr) => {
|
||||
if (err) return t.end(err);
|
||||
t.pass('test data set created');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
0
test/credentials/.keep
Normal file
0
test/credentials/.keep
Normal file
750
test/db/create-and-populate-schema.sql
Normal file
750
test/db/create-and-populate-schema.sql
Normal file
@@ -0,0 +1,750 @@
|
||||
-- MySQL dump 10.13 Distrib 8.0.18, for macos10.14 (x86_64)
|
||||
--
|
||||
-- Host: 127.0.0.1 Database: jambones_test
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 5.7.33
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!50503 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `account_products`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `account_products`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `account_products` (
|
||||
`account_product_sid` char(36) NOT NULL,
|
||||
`account_subscription_sid` char(36) NOT NULL,
|
||||
`product_sid` char(36) NOT NULL,
|
||||
`quantity` int(11) NOT NULL,
|
||||
PRIMARY KEY (`account_product_sid`),
|
||||
UNIQUE KEY `account_product_sid` (`account_product_sid`),
|
||||
KEY `account_product_sid_idx` (`account_product_sid`),
|
||||
KEY `account_subscription_sid_idx` (`account_subscription_sid`),
|
||||
KEY `product_sid_idxfk` (`product_sid`),
|
||||
CONSTRAINT `account_subscription_sid_idxfk` FOREIGN KEY (`account_subscription_sid`) REFERENCES `account_subscriptions` (`account_subscription_sid`),
|
||||
CONSTRAINT `product_sid_idxfk` FOREIGN KEY (`product_sid`) REFERENCES `products` (`product_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `account_products`
|
||||
--
|
||||
|
||||
LOCK TABLES `account_products` WRITE;
|
||||
/*!40000 ALTER TABLE `account_products` DISABLE KEYS */;
|
||||
INSERT INTO `account_products` VALUES ('bb0e8a44-0e59-4103-a44c-f7ff950319fb','02639178-e073-4f8e-9b7e-48b1d36f4b7a','35a9fb10-233d-4eb9-aada-78de5814d680',10),('e2cd5148-07ad-4cdc-b395-22e4b4e23d7e','02639178-e073-4f8e-9b7e-48b1d36f4b7a','2c815913-5c26-4004-b748-183b459329df',10),('f9b320aa-c287-438b-a4c0-e4383b4f0256','02639178-e073-4f8e-9b7e-48b1d36f4b7a','c4403cdb-8e75-4b27-9726-7d8315e3216d',10);
|
||||
/*!40000 ALTER TABLE `account_products` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `account_static_ips`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `account_static_ips`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `account_static_ips` (
|
||||
`account_static_ip_sid` char(36) NOT NULL,
|
||||
`account_sid` char(36) NOT NULL,
|
||||
`ipv4` varchar(16) NOT NULL,
|
||||
`sbc_address_sid` char(36) NOT NULL,
|
||||
PRIMARY KEY (`account_static_ip_sid`),
|
||||
UNIQUE KEY `account_static_ip_sid` (`account_static_ip_sid`),
|
||||
UNIQUE KEY `ipv4` (`ipv4`),
|
||||
KEY `account_static_ip_sid_idx` (`account_static_ip_sid`),
|
||||
KEY `account_sid_idx` (`account_sid`),
|
||||
KEY `sbc_address_sid_idxfk` (`sbc_address_sid`),
|
||||
CONSTRAINT `account_sid_idxfk_3` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`),
|
||||
CONSTRAINT `sbc_address_sid_idxfk` FOREIGN KEY (`sbc_address_sid`) REFERENCES `sbc_addresses` (`sbc_address_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `account_static_ips`
|
||||
--
|
||||
|
||||
LOCK TABLES `account_static_ips` WRITE;
|
||||
/*!40000 ALTER TABLE `account_static_ips` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `account_static_ips` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `account_subscriptions`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `account_subscriptions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `account_subscriptions` (
|
||||
`account_subscription_sid` char(36) NOT NULL,
|
||||
`account_sid` char(36) NOT NULL,
|
||||
`pending` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`effective_start_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`effective_end_date` datetime DEFAULT NULL,
|
||||
`change_reason` varchar(255) DEFAULT NULL,
|
||||
`stripe_subscription_id` varchar(56) DEFAULT NULL,
|
||||
`stripe_payment_method_id` varchar(56) DEFAULT NULL,
|
||||
`stripe_statement_descriptor` varchar(255) DEFAULT NULL,
|
||||
`last4` char(4) DEFAULT NULL,
|
||||
`exp_month` int(11) DEFAULT NULL,
|
||||
`exp_year` int(11) DEFAULT NULL,
|
||||
`card_type` varchar(16) DEFAULT NULL,
|
||||
`pending_reason` varbinary(52) DEFAULT NULL,
|
||||
PRIMARY KEY (`account_subscription_sid`),
|
||||
UNIQUE KEY `account_subscription_sid` (`account_subscription_sid`),
|
||||
KEY `account_subscription_sid_idx` (`account_subscription_sid`),
|
||||
KEY `account_sid_idx` (`account_sid`),
|
||||
CONSTRAINT `account_sid_idxfk` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `account_subscriptions`
|
||||
--
|
||||
|
||||
LOCK TABLES `account_subscriptions` WRITE;
|
||||
/*!40000 ALTER TABLE `account_subscriptions` DISABLE KEYS */;
|
||||
INSERT INTO `account_subscriptions` VALUES ('02639178-e073-4f8e-9b7e-48b1d36f4b7a','bb845d4b-83a9-4cde-a6e9-50f3743bab3f',0,'2021-04-03 15:41:03',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
|
||||
/*!40000 ALTER TABLE `account_subscriptions` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `accounts`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `accounts`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `accounts` (
|
||||
`account_sid` char(36) NOT NULL,
|
||||
`name` varchar(64) NOT NULL,
|
||||
`sip_realm` varchar(132) DEFAULT NULL COMMENT 'sip domain that will be used for devices registering under this account',
|
||||
`service_provider_sid` char(36) NOT NULL COMMENT 'service provider that owns the customer relationship with this account',
|
||||
`registration_hook_sid` char(36) DEFAULT NULL COMMENT 'webhook to call when devices underr this account attempt to register',
|
||||
`device_calling_application_sid` char(36) DEFAULT NULL COMMENT 'application to use for outbound calling from an account',
|
||||
`is_active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`plan_type` enum('trial','free','paid') NOT NULL DEFAULT 'trial',
|
||||
`stripe_customer_id` varchar(56) DEFAULT NULL,
|
||||
`webhook_secret` varchar(36) NOT NULL,
|
||||
`disable_cdrs` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`trial_end_date` datetime DEFAULT NULL,
|
||||
`deactivated_reason` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`account_sid`),
|
||||
UNIQUE KEY `account_sid` (`account_sid`),
|
||||
UNIQUE KEY `sip_realm` (`sip_realm`),
|
||||
KEY `account_sid_idx` (`account_sid`),
|
||||
KEY `sip_realm_idx` (`sip_realm`),
|
||||
KEY `service_provider_sid_idx` (`service_provider_sid`),
|
||||
KEY `registration_hook_sid_idxfk_1` (`registration_hook_sid`),
|
||||
KEY `device_calling_application_sid_idxfk` (`device_calling_application_sid`),
|
||||
CONSTRAINT `device_calling_application_sid_idxfk` FOREIGN KEY (`device_calling_application_sid`) REFERENCES `applications` (`application_sid`),
|
||||
CONSTRAINT `registration_hook_sid_idxfk_1` FOREIGN KEY (`registration_hook_sid`) REFERENCES `webhooks` (`webhook_sid`),
|
||||
CONSTRAINT `service_provider_sid_idxfk_6` FOREIGN KEY (`service_provider_sid`) REFERENCES `service_providers` (`service_provider_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='An enterprise that uses the platform for comm services';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `accounts`
|
||||
--
|
||||
|
||||
LOCK TABLES `accounts` WRITE;
|
||||
/*!40000 ALTER TABLE `accounts` DISABLE KEYS */;
|
||||
INSERT INTO `accounts` VALUES ('bb845d4b-83a9-4cde-a6e9-50f3743bab3f','Joe User','test.yakeeda.com','2708b1b3-2736-40ea-b502-c53d8396247f',NULL,NULL,1,'2021-04-03 15:41:03','trial',NULL,'wh_secret_ehV2dVyzNBs5kHxeJcatRQ',0,NULL,NULL);
|
||||
INSERT INTO `accounts` VALUES ('622f62e4-303a-49f2-bbe0-eb1e1714e37a','Dave Horton','delta.yakeeda.com','2708b1b3-2736-40ea-b502-c53d8396247f',NULL,NULL,0,'2021-04-03 15:41:03','trial',NULL,'wh_secret_ehV2dVyzNBs5kHxeJcatRQ',0,NULL,NULL);
|
||||
/*!40000 ALTER TABLE `accounts` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `api_keys`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `api_keys`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `api_keys` (
|
||||
`api_key_sid` char(36) NOT NULL,
|
||||
`token` char(36) NOT NULL,
|
||||
`account_sid` char(36) DEFAULT NULL,
|
||||
`service_provider_sid` char(36) DEFAULT NULL,
|
||||
`expires_at` timestamp NULL DEFAULT NULL,
|
||||
`last_used` timestamp NULL DEFAULT NULL,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`api_key_sid`),
|
||||
UNIQUE KEY `api_key_sid` (`api_key_sid`),
|
||||
UNIQUE KEY `token` (`token`),
|
||||
KEY `api_key_sid_idx` (`api_key_sid`),
|
||||
KEY `account_sid_idx` (`account_sid`),
|
||||
KEY `service_provider_sid_idx` (`service_provider_sid`),
|
||||
CONSTRAINT `account_sid_idxfk_4` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`),
|
||||
CONSTRAINT `service_provider_sid_idxfk` FOREIGN KEY (`service_provider_sid`) REFERENCES `service_providers` (`service_provider_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='An authorization token that is used to access the REST api';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `api_keys`
|
||||
--
|
||||
|
||||
LOCK TABLES `api_keys` WRITE;
|
||||
/*!40000 ALTER TABLE `api_keys` DISABLE KEYS */;
|
||||
INSERT INTO `api_keys` VALUES ('3f35518f-5a0d-4c2e-90a5-2407bb3b36f0','38700987-c7a4-4685-a5bb-af378f9734de',NULL,NULL,NULL,NULL,'2021-04-03 15:40:37'),('b00b1025-2b65-453b-a243-599b75be7d0a','52c2eb45-9f72-4545-9c60-9639e3f4eaf7','bb845d4b-83a9-4cde-a6e9-50f3743bab3f',NULL,NULL,NULL,'2021-04-03 15:42:40');
|
||||
/*!40000 ALTER TABLE `api_keys` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `applications`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `applications`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `applications` (
|
||||
`application_sid` char(36) NOT NULL,
|
||||
`name` varchar(64) NOT NULL,
|
||||
`service_provider_sid` char(36) DEFAULT NULL COMMENT 'if non-null, this application is a test application that can be used by any account under the associated service provider',
|
||||
`account_sid` char(36) DEFAULT NULL COMMENT 'account that this application belongs to (if null, this is a service provider test application)',
|
||||
`call_hook_sid` char(36) DEFAULT NULL COMMENT 'webhook to call for inbound calls ',
|
||||
`call_status_hook_sid` char(36) DEFAULT NULL COMMENT 'webhook to call for call status events',
|
||||
`messaging_hook_sid` char(36) DEFAULT NULL COMMENT 'webhook to call for inbound SMS/MMS ',
|
||||
`speech_synthesis_vendor` varchar(64) NOT NULL DEFAULT 'google',
|
||||
`speech_synthesis_language` varchar(12) NOT NULL DEFAULT 'en-US',
|
||||
`speech_synthesis_voice` varchar(64) DEFAULT NULL,
|
||||
`speech_recognizer_vendor` varchar(64) NOT NULL DEFAULT 'google',
|
||||
`speech_recognizer_language` varchar(64) NOT NULL DEFAULT 'en-US',
|
||||
PRIMARY KEY (`application_sid`),
|
||||
UNIQUE KEY `application_sid` (`application_sid`),
|
||||
UNIQUE KEY `applications_idx_name` (`account_sid`,`name`),
|
||||
KEY `application_sid_idx` (`application_sid`),
|
||||
KEY `service_provider_sid_idx` (`service_provider_sid`),
|
||||
KEY `account_sid_idx` (`account_sid`),
|
||||
KEY `call_hook_sid_idxfk` (`call_hook_sid`),
|
||||
KEY `call_status_hook_sid_idxfk` (`call_status_hook_sid`),
|
||||
KEY `messaging_hook_sid_idxfk` (`messaging_hook_sid`),
|
||||
CONSTRAINT `account_sid_idxfk_10` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`),
|
||||
CONSTRAINT `call_hook_sid_idxfk` FOREIGN KEY (`call_hook_sid`) REFERENCES `webhooks` (`webhook_sid`),
|
||||
CONSTRAINT `call_status_hook_sid_idxfk` FOREIGN KEY (`call_status_hook_sid`) REFERENCES `webhooks` (`webhook_sid`),
|
||||
CONSTRAINT `messaging_hook_sid_idxfk` FOREIGN KEY (`messaging_hook_sid`) REFERENCES `webhooks` (`webhook_sid`),
|
||||
CONSTRAINT `service_provider_sid_idxfk_5` FOREIGN KEY (`service_provider_sid`) REFERENCES `service_providers` (`service_provider_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='A defined set of behaviors to be applied to phone calls ';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `applications`
|
||||
--
|
||||
|
||||
LOCK TABLES `applications` WRITE;
|
||||
/*!40000 ALTER TABLE `applications` DISABLE KEYS */;
|
||||
INSERT INTO `applications` VALUES ('0dddaabf-0a30-43e3-84e8-426873b1a78b','decline call',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f','c71e79db-24f2-4866-a3ee-febb0f97b341','293904c1-351b-4bca-8d58-1a29b853c7db',NULL,'google','en-US','en-US-Standard-C','google','en-US');
|
||||
INSERT INTO `applications` VALUES ('308b4f41-1a18-4052-b89a-c054e75ce242','say',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f','54ab0976-a6c0-45d8-89a4-d90d45bf9d96','293904c1-351b-4bca-8d58-1a29b853c7db',NULL,'google','en-US','en-US-Standard-C','google','en-US');
|
||||
INSERT INTO `applications` VALUES ('24d0f6af-e976-44dd-a2e8-41c7b55abe33','say account 2',NULL,'622f62e4-303a-49f2-bbe0-eb1e1714e37a','54ab0976-a6c0-45d8-89a4-d90d45bf9d96','293904c1-351b-4bca-8d58-1a29b853c7db',NULL,'google','en-US','en-US-Standard-C','google','en-US');
|
||||
INSERT INTO `applications` VALUES ('17461c69-56b5-4dab-ad83-1c43a0f93a3d','gather',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f','10692465-a511-4277-9807-b7157e4f81e1','293904c1-351b-4bca-8d58-1a29b853c7db',NULL,'google','en-US','en-US-Standard-C','google','en-US');
|
||||
INSERT INTO `applications` VALUES ('baf9213b-5556-4c20-870c-586392ed246f','transcribe',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f','ecb67a8f-f7ce-4919-abf0-bbc69c1001e5','293904c1-351b-4bca-8d58-1a29b853c7db',NULL,'google','en-US','en-US-Standard-C','google','en-US');
|
||||
INSERT INTO `applications` VALUES ('ae026ab5-3029-47b4-9d7c-236e3a4b4ebe','transcribe account 2',NULL,'622f62e4-303a-49f2-bbe0-eb1e1714e37a','ecb67a8f-f7ce-4919-abf0-bbc69c1001e5','293904c1-351b-4bca-8d58-1a29b853c7db',NULL,'google','en-US','en-US-Standard-C','google','en-US');
|
||||
/*!40000 ALTER TABLE `applications` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `call_routes`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `call_routes`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `call_routes` (
|
||||
`call_route_sid` char(36) NOT NULL,
|
||||
`priority` int(11) NOT NULL,
|
||||
`account_sid` char(36) NOT NULL,
|
||||
`regex` varchar(255) NOT NULL,
|
||||
`application_sid` char(36) NOT NULL,
|
||||
PRIMARY KEY (`call_route_sid`),
|
||||
UNIQUE KEY `call_route_sid` (`call_route_sid`),
|
||||
KEY `call_route_sid_idx` (`call_route_sid`),
|
||||
KEY `account_sid_idxfk_1` (`account_sid`),
|
||||
KEY `application_sid_idxfk` (`application_sid`),
|
||||
CONSTRAINT `account_sid_idxfk_1` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`),
|
||||
CONSTRAINT `application_sid_idxfk` FOREIGN KEY (`application_sid`) REFERENCES `applications` (`application_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='a regex-based pattern match for call routing';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `call_routes`
|
||||
--
|
||||
|
||||
LOCK TABLES `call_routes` WRITE;
|
||||
/*!40000 ALTER TABLE `call_routes` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `call_routes` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `dns_records`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `dns_records`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `dns_records` (
|
||||
`dns_record_sid` char(36) NOT NULL,
|
||||
`account_sid` char(36) NOT NULL,
|
||||
`record_type` varchar(6) NOT NULL,
|
||||
`record_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`dns_record_sid`),
|
||||
UNIQUE KEY `dns_record_sid` (`dns_record_sid`),
|
||||
KEY `dns_record_sid_idx` (`dns_record_sid`),
|
||||
KEY `account_sid_idxfk_2` (`account_sid`),
|
||||
CONSTRAINT `account_sid_idxfk_2` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `dns_records`
|
||||
--
|
||||
|
||||
LOCK TABLES `dns_records` WRITE;
|
||||
/*!40000 ALTER TABLE `dns_records` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `dns_records` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `lcr_carrier_set_entry`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `lcr_carrier_set_entry`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `lcr_carrier_set_entry` (
|
||||
`lcr_carrier_set_entry_sid` char(36) NOT NULL,
|
||||
`workload` int(11) NOT NULL DEFAULT '1' COMMENT 'represents a proportion of traffic to send through the associated carrier; can be used for load balancing traffic across carriers with a common priority for a destination',
|
||||
`lcr_route_sid` char(36) NOT NULL,
|
||||
`voip_carrier_sid` char(36) NOT NULL,
|
||||
`priority` int(11) NOT NULL DEFAULT '0' COMMENT 'lower priority carriers are attempted first',
|
||||
PRIMARY KEY (`lcr_carrier_set_entry_sid`),
|
||||
KEY `lcr_route_sid_idxfk` (`lcr_route_sid`),
|
||||
KEY `voip_carrier_sid_idxfk_2` (`voip_carrier_sid`),
|
||||
CONSTRAINT `lcr_route_sid_idxfk` FOREIGN KEY (`lcr_route_sid`) REFERENCES `lcr_routes` (`lcr_route_sid`),
|
||||
CONSTRAINT `voip_carrier_sid_idxfk_2` FOREIGN KEY (`voip_carrier_sid`) REFERENCES `voip_carriers` (`voip_carrier_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='An entry in the LCR routing list';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `lcr_carrier_set_entry`
|
||||
--
|
||||
|
||||
LOCK TABLES `lcr_carrier_set_entry` WRITE;
|
||||
/*!40000 ALTER TABLE `lcr_carrier_set_entry` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `lcr_carrier_set_entry` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `lcr_routes`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `lcr_routes`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `lcr_routes` (
|
||||
`lcr_route_sid` char(36) NOT NULL,
|
||||
`regex` varchar(32) NOT NULL COMMENT 'regex-based pattern match against dialed number, used for LCR routing of PSTN calls',
|
||||
`description` varchar(1024) DEFAULT NULL,
|
||||
`priority` int(11) NOT NULL COMMENT 'lower priority routes are attempted first',
|
||||
PRIMARY KEY (`lcr_route_sid`),
|
||||
UNIQUE KEY `priority` (`priority`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Least cost routing table';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `lcr_routes`
|
||||
--
|
||||
|
||||
LOCK TABLES `lcr_routes` WRITE;
|
||||
/*!40000 ALTER TABLE `lcr_routes` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `lcr_routes` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `ms_teams_tenants`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `ms_teams_tenants`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `ms_teams_tenants` (
|
||||
`ms_teams_tenant_sid` char(36) NOT NULL,
|
||||
`service_provider_sid` char(36) NOT NULL,
|
||||
`account_sid` char(36) NOT NULL,
|
||||
`application_sid` char(36) DEFAULT NULL,
|
||||
`tenant_fqdn` varchar(255) NOT NULL,
|
||||
PRIMARY KEY (`ms_teams_tenant_sid`),
|
||||
UNIQUE KEY `ms_teams_tenant_sid` (`ms_teams_tenant_sid`),
|
||||
UNIQUE KEY `tenant_fqdn` (`tenant_fqdn`),
|
||||
KEY `ms_teams_tenant_sid_idx` (`ms_teams_tenant_sid`),
|
||||
KEY `service_provider_sid_idxfk_1` (`service_provider_sid`),
|
||||
KEY `account_sid_idxfk_5` (`account_sid`),
|
||||
KEY `application_sid_idxfk_1` (`application_sid`),
|
||||
KEY `tenant_fqdn_idx` (`tenant_fqdn`),
|
||||
CONSTRAINT `account_sid_idxfk_5` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`),
|
||||
CONSTRAINT `application_sid_idxfk_1` FOREIGN KEY (`application_sid`) REFERENCES `applications` (`application_sid`),
|
||||
CONSTRAINT `service_provider_sid_idxfk_1` FOREIGN KEY (`service_provider_sid`) REFERENCES `service_providers` (`service_provider_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='A Microsoft Teams customer tenant';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `ms_teams_tenants`
|
||||
--
|
||||
|
||||
LOCK TABLES `ms_teams_tenants` WRITE;
|
||||
/*!40000 ALTER TABLE `ms_teams_tenants` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `ms_teams_tenants` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `phone_numbers`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `phone_numbers`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `phone_numbers` (
|
||||
`phone_number_sid` char(36) NOT NULL,
|
||||
`number` varchar(32) NOT NULL,
|
||||
`voip_carrier_sid` char(36) DEFAULT NULL,
|
||||
`account_sid` char(36) DEFAULT NULL,
|
||||
`application_sid` char(36) DEFAULT NULL,
|
||||
`service_provider_sid` char(36) DEFAULT NULL COMMENT 'if not null, this number is a test number for the associated service provider',
|
||||
PRIMARY KEY (`phone_number_sid`),
|
||||
UNIQUE KEY `number` (`number`),
|
||||
UNIQUE KEY `phone_number_sid` (`phone_number_sid`),
|
||||
KEY `phone_number_sid_idx` (`phone_number_sid`),
|
||||
KEY `number_idx` (`number`),
|
||||
KEY `voip_carrier_sid_idx` (`voip_carrier_sid`),
|
||||
KEY `account_sid_idxfk_9` (`account_sid`),
|
||||
KEY `application_sid_idxfk_3` (`application_sid`),
|
||||
KEY `service_provider_sid_idx` (`service_provider_sid`),
|
||||
CONSTRAINT `account_sid_idxfk_9` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`),
|
||||
CONSTRAINT `application_sid_idxfk_3` FOREIGN KEY (`application_sid`) REFERENCES `applications` (`application_sid`),
|
||||
CONSTRAINT `service_provider_sid_idxfk_4` FOREIGN KEY (`service_provider_sid`) REFERENCES `service_providers` (`service_provider_sid`),
|
||||
CONSTRAINT `voip_carrier_sid_idxfk` FOREIGN KEY (`voip_carrier_sid`) REFERENCES `voip_carriers` (`voip_carrier_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='A phone number that has been assigned to an account';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `phone_numbers`
|
||||
--
|
||||
|
||||
LOCK TABLES `phone_numbers` WRITE;
|
||||
/*!40000 ALTER TABLE `phone_numbers` DISABLE KEYS */;
|
||||
INSERT INTO `phone_numbers` VALUES ('4b439355-debc-40c7-9cfa-5be58c2bed6b','16174000000','5145b436-2f38-4029-8d4c-fd8c67831c7a','bb845d4b-83a9-4cde-a6e9-50f3743bab3f','0dddaabf-0a30-43e3-84e8-426873b1a78b', NULL);
|
||||
INSERT INTO `phone_numbers` VALUES ('9cc9e7fc-b7b0-4101-8f3c-9fe13ce5df0a','16174000001','5145b436-2f38-4029-8d4c-fd8c67831c7a','bb845d4b-83a9-4cde-a6e9-50f3743bab3f','308b4f41-1a18-4052-b89a-c054e75ce242', NULL);
|
||||
INSERT INTO `phone_numbers` VALUES ('e686a320-0725-418f-be65-532159bdc3ed','16174000002','5145b436-2f38-4029-8d4c-fd8c67831c7a','622f62e4-303a-49f2-bbe0-eb1e1714e37a','24d0f6af-e976-44dd-a2e8-41c7b55abe33', NULL);
|
||||
INSERT INTO `phone_numbers` VALUES ('05eeed62-b29b-4679-bf38-d7a4e318be44','16174000003','5145b436-2f38-4029-8d4c-fd8c67831c7a','bb845d4b-83a9-4cde-a6e9-50f3743bab3f','17461c69-56b5-4dab-ad83-1c43a0f93a3d', NULL);
|
||||
INSERT INTO `phone_numbers` VALUES ('f3c53863-b629-4cf6-9dcb-c7fb7072314b','16174000004','5145b436-2f38-4029-8d4c-fd8c67831c7a','bb845d4b-83a9-4cde-a6e9-50f3743bab3f','baf9213b-5556-4c20-870c-586392ed246f', NULL);
|
||||
INSERT INTO `phone_numbers` VALUES ('f6416c17-829a-4f11-9c32-f0d00e4a9ae9','16174000005','5145b436-2f38-4029-8d4c-fd8c67831c7a','622f62e4-303a-49f2-bbe0-eb1e1714e37a','ae026ab5-3029-47b4-9d7c-236e3a4b4ebe', NULL);
|
||||
/*!40000 ALTER TABLE `phone_numbers` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `products`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `products`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `products` (
|
||||
`product_sid` char(36) NOT NULL,
|
||||
`name` varchar(32) NOT NULL,
|
||||
`category` enum('api_rate','voice_call_session','device') NOT NULL,
|
||||
PRIMARY KEY (`product_sid`),
|
||||
UNIQUE KEY `product_sid` (`product_sid`),
|
||||
KEY `product_sid_idx` (`product_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `products`
|
||||
--
|
||||
|
||||
LOCK TABLES `products` WRITE;
|
||||
/*!40000 ALTER TABLE `products` DISABLE KEYS */;
|
||||
INSERT INTO `products` VALUES ('2c815913-5c26-4004-b748-183b459329df','registered device','device'),('35a9fb10-233d-4eb9-aada-78de5814d680','api call','api_rate'),('c4403cdb-8e75-4b27-9726-7d8315e3216d','concurrent call session','voice_call_session');
|
||||
/*!40000 ALTER TABLE `products` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `sbc_addresses`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `sbc_addresses`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `sbc_addresses` (
|
||||
`sbc_address_sid` char(36) NOT NULL,
|
||||
`ipv4` varchar(255) NOT NULL,
|
||||
`port` int(11) NOT NULL DEFAULT '5060',
|
||||
`service_provider_sid` char(36) DEFAULT NULL,
|
||||
PRIMARY KEY (`sbc_address_sid`),
|
||||
UNIQUE KEY `sbc_address_sid` (`sbc_address_sid`),
|
||||
KEY `sbc_addresses_idx_host_port` (`ipv4`,`port`),
|
||||
KEY `sbc_address_sid_idx` (`sbc_address_sid`),
|
||||
KEY `service_provider_sid_idx` (`service_provider_sid`),
|
||||
CONSTRAINT `service_provider_sid_idxfk_2` FOREIGN KEY (`service_provider_sid`) REFERENCES `service_providers` (`service_provider_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `sbc_addresses`
|
||||
--
|
||||
|
||||
LOCK TABLES `sbc_addresses` WRITE;
|
||||
/*!40000 ALTER TABLE `sbc_addresses` DISABLE KEYS */;
|
||||
INSERT INTO `sbc_addresses` VALUES ('8d6d0fda-4550-41ab-8e2f-60761d81fe7d','3.39.45.30',5060,NULL);
|
||||
/*!40000 ALTER TABLE `sbc_addresses` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `service_providers`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `service_providers`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `service_providers` (
|
||||
`service_provider_sid` char(36) NOT NULL,
|
||||
`name` varchar(64) NOT NULL,
|
||||
`description` varchar(255) DEFAULT NULL,
|
||||
`root_domain` varchar(128) DEFAULT NULL,
|
||||
`registration_hook_sid` char(36) DEFAULT NULL,
|
||||
`ms_teams_fqdn` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`service_provider_sid`),
|
||||
UNIQUE KEY `service_provider_sid` (`service_provider_sid`),
|
||||
UNIQUE KEY `name` (`name`),
|
||||
UNIQUE KEY `root_domain` (`root_domain`),
|
||||
KEY `service_provider_sid_idx` (`service_provider_sid`),
|
||||
KEY `name_idx` (`name`),
|
||||
KEY `root_domain_idx` (`root_domain`),
|
||||
KEY `registration_hook_sid_idxfk` (`registration_hook_sid`),
|
||||
CONSTRAINT `registration_hook_sid_idxfk` FOREIGN KEY (`registration_hook_sid`) REFERENCES `webhooks` (`webhook_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='A partition of the platform used by one service provider';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `service_providers`
|
||||
--
|
||||
|
||||
LOCK TABLES `service_providers` WRITE;
|
||||
/*!40000 ALTER TABLE `service_providers` DISABLE KEYS */;
|
||||
INSERT INTO `service_providers` VALUES ('2708b1b3-2736-40ea-b502-c53d8396247f','jambonz.us','jambonz.us service provider','yakeeda.com',NULL,NULL);
|
||||
/*!40000 ALTER TABLE `service_providers` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `sip_gateways`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `sip_gateways`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `sip_gateways` (
|
||||
`sip_gateway_sid` char(36) NOT NULL,
|
||||
`ipv4` varchar(128) NOT NULL COMMENT 'ip address or DNS name of the gateway. For gateways providing inbound calling service, ip address is required.',
|
||||
`port` int(11) NOT NULL DEFAULT '5060' COMMENT 'sip signaling port',
|
||||
`inbound` tinyint(1) NOT NULL COMMENT 'if true, whitelist this IP to allow inbound calls from the gateway',
|
||||
`outbound` tinyint(1) NOT NULL COMMENT 'if true, include in least-cost routing when placing calls to the PSTN',
|
||||
`voip_carrier_sid` char(36) NOT NULL,
|
||||
`is_active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`sip_gateway_sid`),
|
||||
KEY `sip_gateway_idx_hostport` (`ipv4`,`port`),
|
||||
KEY `voip_carrier_sid_idx` (`voip_carrier_sid`),
|
||||
CONSTRAINT `voip_carrier_sid_idxfk_1` FOREIGN KEY (`voip_carrier_sid`) REFERENCES `voip_carriers` (`voip_carrier_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='A whitelisted sip gateway used for origination/termination';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `sip_gateways`
|
||||
--
|
||||
|
||||
LOCK TABLES `sip_gateways` WRITE;
|
||||
/*!40000 ALTER TABLE `sip_gateways` DISABLE KEYS */;
|
||||
INSERT INTO `sip_gateways` VALUES ('46b727eb-c7dc-44fa-b063-96e48d408e4a','3.3.3.3',5060,1,1,'5145b436-2f38-4029-8d4c-fd8c67831c7a',1),('81629182-6904-4588-8c72-a78d70053fb9','54.172.60.1',5060,1,1,'df0aefbf-ca7b-4d48-9fbf-3c66fef72060',1);
|
||||
/*!40000 ALTER TABLE `sip_gateways` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `speech_credentials`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `speech_credentials`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `speech_credentials` (
|
||||
`speech_credential_sid` char(36) NOT NULL,
|
||||
`account_sid` char(36) NOT NULL,
|
||||
`vendor` varchar(255) NOT NULL,
|
||||
`credential` VARCHAR(8192) NOT NULL,
|
||||
`use_for_tts` tinyint(1) DEFAULT '1',
|
||||
`use_for_stt` tinyint(1) DEFAULT '1',
|
||||
`last_used` datetime DEFAULT NULL,
|
||||
`last_tested` datetime DEFAULT NULL,
|
||||
`tts_tested_ok` tinyint(1) DEFAULT NULL,
|
||||
`stt_tested_ok` tinyint(1) DEFAULT NULL,
|
||||
PRIMARY KEY (`speech_credential_sid`),
|
||||
UNIQUE KEY `speech_credential_sid` (`speech_credential_sid`),
|
||||
UNIQUE KEY `speech_credentials_idx_1` (`vendor`,`account_sid`),
|
||||
KEY `speech_credential_sid_idx` (`speech_credential_sid`),
|
||||
KEY `account_sid_idx` (`account_sid`),
|
||||
CONSTRAINT `account_sid_idxfk_6` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `speech_credentials`
|
||||
--
|
||||
|
||||
LOCK TABLES `speech_credentials` WRITE;
|
||||
/*!40000 ALTER TABLE `speech_credentials` DISABLE KEYS */;
|
||||
INSERT INTO `speech_credentials` VALUES ('2add163c-34f2-45c6-a016-f955d218ffb6','bb845d4b-83a9-4cde-a6e9-50f3743bab3f','google','credential-goes-here',1,1,NULL,'2021-04-03 15:42:10',1,1),('84154212-5c99-4c94-8993-bc2a46288daa','bb845d4b-83a9-4cde-a6e9-50f3743bab3f','aws','credential-goes-here',0,0,NULL,NULL,NULL,NULL);
|
||||
/*!40000 ALTER TABLE `speech_credentials` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `users`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `users` (
|
||||
`user_sid` char(36) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`email` varchar(255) NOT NULL,
|
||||
`pending_email` varchar(255) DEFAULT NULL,
|
||||
`phone` varchar(20) DEFAULT NULL,
|
||||
`hashed_password` varchar(1024) DEFAULT NULL,
|
||||
`salt` char(16) DEFAULT NULL,
|
||||
`account_sid` char(36) DEFAULT NULL,
|
||||
`service_provider_sid` char(36) DEFAULT NULL,
|
||||
`force_change` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`provider` varchar(255) NOT NULL,
|
||||
`provider_userid` varchar(255) DEFAULT NULL,
|
||||
`scope` varchar(16) NOT NULL DEFAULT 'read-write',
|
||||
`phone_activation_code` varchar(16) DEFAULT NULL,
|
||||
`email_activation_code` varchar(16) DEFAULT NULL,
|
||||
`email_validated` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`phone_validated` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`email_content_opt_out` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`user_sid`),
|
||||
UNIQUE KEY `user_sid` (`user_sid`),
|
||||
UNIQUE KEY `phone` (`phone`),
|
||||
KEY `user_sid_idx` (`user_sid`),
|
||||
KEY `email_idx` (`email`),
|
||||
KEY `phone_idx` (`phone`),
|
||||
KEY `account_sid_idx` (`account_sid`),
|
||||
KEY `service_provider_sid_idx` (`service_provider_sid`),
|
||||
KEY `email_activation_code_idx` (`email_activation_code`),
|
||||
CONSTRAINT `account_sid_idxfk_7` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`),
|
||||
CONSTRAINT `service_provider_sid_idxfk_3` FOREIGN KEY (`service_provider_sid`) REFERENCES `service_providers` (`service_provider_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `users`
|
||||
--
|
||||
|
||||
LOCK TABLES `users` WRITE;
|
||||
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
|
||||
INSERT INTO `users` VALUES ('d9cdf199-78d1-4f92-b717-5f9dbdf56565','Dave Horton','daveh@drachtio.org',NULL,NULL,NULL,NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f',NULL,0,'github','davehorton','read-write',NULL,NULL,1,0,0);
|
||||
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `voip_carriers`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `voip_carriers`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `voip_carriers` (
|
||||
`voip_carrier_sid` char(36) NOT NULL,
|
||||
`name` varchar(64) NOT NULL,
|
||||
`description` varchar(255) DEFAULT NULL,
|
||||
`account_sid` char(36) DEFAULT NULL COMMENT 'if provided, indicates this entity represents a sip trunk that is associated with a specific account',
|
||||
`application_sid` char(36) DEFAULT NULL COMMENT 'If provided, all incoming calls from this source will be routed to the associated application',
|
||||
`e164_leading_plus` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'if true, a leading plus should be prepended to outbound phone numbers',
|
||||
`requires_register` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`register_username` varchar(64) DEFAULT NULL,
|
||||
`register_sip_realm` varchar(64) DEFAULT NULL,
|
||||
`register_password` varchar(64) DEFAULT NULL,
|
||||
`tech_prefix` varchar(16) DEFAULT NULL COMMENT 'tech prefix to prepend to outbound calls to this carrier',
|
||||
PRIMARY KEY (`voip_carrier_sid`),
|
||||
UNIQUE KEY `voip_carrier_sid` (`voip_carrier_sid`),
|
||||
KEY `voip_carrier_sid_idx` (`voip_carrier_sid`),
|
||||
KEY `account_sid_idx` (`account_sid`),
|
||||
KEY `application_sid_idxfk_2` (`application_sid`),
|
||||
CONSTRAINT `account_sid_idxfk_8` FOREIGN KEY (`account_sid`) REFERENCES `accounts` (`account_sid`),
|
||||
CONSTRAINT `application_sid_idxfk_2` FOREIGN KEY (`application_sid`) REFERENCES `applications` (`application_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='A Carrier or customer PBX that can send or receive calls';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `voip_carriers`
|
||||
--
|
||||
|
||||
LOCK TABLES `voip_carriers` WRITE;
|
||||
/*!40000 ALTER TABLE `voip_carriers` DISABLE KEYS */;
|
||||
INSERT INTO `voip_carriers` VALUES ('5145b436-2f38-4029-8d4c-fd8c67831c7a','my test carrier',NULL,NULL,NULL,0,0,NULL,NULL,NULL,NULL),('df0aefbf-ca7b-4d48-9fbf-3c66fef72060','my test carrier',NULL,'bb845d4b-83a9-4cde-a6e9-50f3743bab3f',NULL,0,0,NULL,NULL,NULL,NULL);
|
||||
/*!40000 ALTER TABLE `voip_carriers` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `webhooks`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `webhooks`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `webhooks` (
|
||||
`webhook_sid` char(36) NOT NULL,
|
||||
`url` varchar(1024) NOT NULL,
|
||||
`method` enum('GET','POST') NOT NULL DEFAULT 'POST',
|
||||
`username` varchar(255) DEFAULT NULL,
|
||||
`password` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`webhook_sid`),
|
||||
UNIQUE KEY `webhook_sid` (`webhook_sid`),
|
||||
KEY `webhook_sid_idx` (`webhook_sid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='An HTTP callback';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `webhooks`
|
||||
--
|
||||
|
||||
LOCK TABLES `webhooks` WRITE;
|
||||
/*!40000 ALTER TABLE `webhooks` DISABLE KEYS */;
|
||||
INSERT INTO `webhooks` VALUES ('6ac36aeb-6bd0-428a-80a1-aed95640a296','https://flows.jambonz.us/callStatus','POST',NULL,NULL),('d9c205c6-a129-443e-a9c0-d1bb437d4bb7','https://flows.jambonz.us/testCall','POST',NULL,NULL);
|
||||
INSERT INTO `webhooks` VALUES ('293904c1-351b-4bca-8d58-1a29b853c7db','http://127.0.0.1:3100/callStatus','POST',NULL,NULL);
|
||||
INSERT INTO `webhooks` VALUES ('c71e79db-24f2-4866-a3ee-febb0f97b341','http://127.0.0.1:3100/','POST',NULL,NULL);
|
||||
INSERT INTO `webhooks` VALUES ('54ab0976-a6c0-45d8-89a4-d90d45bf9d96','http://127.0.0.1:3101/','POST',NULL,NULL);
|
||||
INSERT INTO `webhooks` VALUES ('10692465-a511-4277-9807-b7157e4f81e1','http://127.0.0.1:3102/','POST',NULL,NULL);
|
||||
INSERT INTO `webhooks` VALUES ('ecb67a8f-f7ce-4919-abf0-bbc69c1001e5','http://127.0.0.1:3103/','POST',NULL,NULL);
|
||||
/*!40000 ALTER TABLE `webhooks` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2021-04-03 11:50:25
|
||||
@@ -1,3 +1,3 @@
|
||||
create database jambones_test;
|
||||
create user jambones_test@localhost IDENTIFIED WITH mysql_native_password by 'jambones_test';
|
||||
grant all on jambones_test.* to jambones_test@localhost;
|
||||
create user jambones_test@'%' IDENTIFIED WITH mysql_native_password by 'jambones_test';
|
||||
grant all on jambones_test.* to jambones_test@'%';
|
||||
|
||||
@@ -2,20 +2,46 @@
|
||||
|
||||
SET FOREIGN_KEY_CHECKS=0;
|
||||
|
||||
DROP TABLE IF EXISTS account_static_ips;
|
||||
|
||||
DROP TABLE IF EXISTS account_products;
|
||||
|
||||
DROP TABLE IF EXISTS account_subscriptions;
|
||||
|
||||
DROP TABLE IF EXISTS beta_invite_codes;
|
||||
|
||||
DROP TABLE IF EXISTS call_routes;
|
||||
|
||||
DROP TABLE IF EXISTS dns_records;
|
||||
|
||||
DROP TABLE IF EXISTS lcr_carrier_set_entry;
|
||||
|
||||
DROP TABLE IF EXISTS lcr_routes;
|
||||
|
||||
DROP TABLE IF EXISTS api_keys;
|
||||
DROP TABLE IF EXISTS predefined_sip_gateways;
|
||||
|
||||
DROP TABLE IF EXISTS ms_teams_tenants;
|
||||
DROP TABLE IF EXISTS predefined_carriers;
|
||||
|
||||
DROP TABLE IF EXISTS account_offers;
|
||||
|
||||
DROP TABLE IF EXISTS products;
|
||||
|
||||
DROP TABLE IF EXISTS api_keys;
|
||||
|
||||
DROP TABLE IF EXISTS sbc_addresses;
|
||||
|
||||
DROP TABLE IF EXISTS ms_teams_tenants;
|
||||
|
||||
DROP TABLE IF EXISTS signup_history;
|
||||
|
||||
DROP TABLE IF EXISTS smpp_addresses;
|
||||
|
||||
DROP TABLE IF EXISTS speech_credentials;
|
||||
|
||||
DROP TABLE IF EXISTS users;
|
||||
|
||||
DROP TABLE IF EXISTS smpp_gateways;
|
||||
|
||||
DROP TABLE IF EXISTS phone_numbers;
|
||||
|
||||
DROP TABLE IF EXISTS sip_gateways;
|
||||
@@ -30,6 +56,41 @@ DROP TABLE IF EXISTS service_providers;
|
||||
|
||||
DROP TABLE IF EXISTS webhooks;
|
||||
|
||||
CREATE TABLE account_static_ips
|
||||
(
|
||||
account_static_ip_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
account_sid CHAR(36) NOT NULL,
|
||||
public_ipv4 VARCHAR(16) NOT NULL UNIQUE ,
|
||||
private_ipv4 VARBINARY(16) NOT NULL UNIQUE ,
|
||||
PRIMARY KEY (account_static_ip_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE account_subscriptions
|
||||
(
|
||||
account_subscription_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
account_sid CHAR(36) NOT NULL,
|
||||
pending BOOLEAN NOT NULL DEFAULT false,
|
||||
effective_start_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
effective_end_date DATETIME,
|
||||
change_reason VARCHAR(255),
|
||||
stripe_subscription_id VARCHAR(56),
|
||||
stripe_payment_method_id VARCHAR(56),
|
||||
stripe_statement_descriptor VARCHAR(255),
|
||||
last4 VARCHAR(512),
|
||||
exp_month INTEGER,
|
||||
exp_year INTEGER,
|
||||
card_type VARCHAR(16),
|
||||
pending_reason VARBINARY(52),
|
||||
PRIMARY KEY (account_subscription_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE beta_invite_codes
|
||||
(
|
||||
invite_code CHAR(6) NOT NULL UNIQUE ,
|
||||
in_use BOOLEAN NOT NULL DEFAULT false,
|
||||
PRIMARY KEY (invite_code)
|
||||
);
|
||||
|
||||
CREATE TABLE call_routes
|
||||
(
|
||||
call_route_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
@@ -38,7 +99,16 @@ account_sid CHAR(36) NOT NULL,
|
||||
regex VARCHAR(255) NOT NULL,
|
||||
application_sid CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (call_route_sid)
|
||||
) ENGINE=InnoDB COMMENT='a regex-based pattern match for call routing';
|
||||
) COMMENT='a regex-based pattern match for call routing';
|
||||
|
||||
CREATE TABLE dns_records
|
||||
(
|
||||
dns_record_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
account_sid CHAR(36) NOT NULL,
|
||||
record_type VARCHAR(6) NOT NULL,
|
||||
record_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (dns_record_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE lcr_routes
|
||||
(
|
||||
@@ -49,15 +119,81 @@ priority INTEGER NOT NULL UNIQUE COMMENT 'lower priority routes are attempted f
|
||||
PRIMARY KEY (lcr_route_sid)
|
||||
) COMMENT='Least cost routing table';
|
||||
|
||||
CREATE TABLE predefined_carriers
|
||||
(
|
||||
predefined_carrier_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
requires_static_ip BOOLEAN NOT NULL DEFAULT false,
|
||||
e164_leading_plus BOOLEAN NOT NULL DEFAULT false COMMENT 'if true, a leading plus should be prepended to outbound phone numbers',
|
||||
requires_register BOOLEAN NOT NULL DEFAULT false,
|
||||
register_username VARCHAR(64),
|
||||
register_sip_realm VARCHAR(64),
|
||||
register_password VARCHAR(64),
|
||||
tech_prefix VARCHAR(16) COMMENT 'tech prefix to prepend to outbound calls to this carrier',
|
||||
inbound_auth_username VARCHAR(64),
|
||||
inbound_auth_password VARCHAR(64),
|
||||
diversion VARCHAR(32),
|
||||
PRIMARY KEY (predefined_carrier_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE predefined_sip_gateways
|
||||
(
|
||||
predefined_sip_gateway_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
ipv4 VARCHAR(128) NOT NULL COMMENT 'ip address or DNS name of the gateway. For gateways providing inbound calling service, ip address is required.',
|
||||
port INTEGER NOT NULL DEFAULT 5060 COMMENT 'sip signaling port',
|
||||
inbound BOOLEAN NOT NULL COMMENT 'if true, whitelist this IP to allow inbound calls from the gateway',
|
||||
outbound BOOLEAN NOT NULL COMMENT 'if true, include in least-cost routing when placing calls to the PSTN',
|
||||
netmask INTEGER NOT NULL DEFAULT 32,
|
||||
predefined_carrier_sid CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (predefined_sip_gateway_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE products
|
||||
(
|
||||
product_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
name VARCHAR(32) NOT NULL,
|
||||
category ENUM('api_rate','voice_call_session', 'device') NOT NULL,
|
||||
PRIMARY KEY (product_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE account_products
|
||||
(
|
||||
account_product_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
account_subscription_sid CHAR(36) NOT NULL,
|
||||
product_sid CHAR(36) NOT NULL,
|
||||
quantity INTEGER NOT NULL,
|
||||
PRIMARY KEY (account_product_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE account_offers
|
||||
(
|
||||
account_offer_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
account_sid CHAR(36) NOT NULL,
|
||||
product_sid CHAR(36) NOT NULL,
|
||||
stripe_product_id VARCHAR(56) NOT NULL,
|
||||
PRIMARY KEY (account_offer_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE api_keys
|
||||
(
|
||||
api_key_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
token CHAR(36) NOT NULL UNIQUE ,
|
||||
account_sid CHAR(36),
|
||||
service_provider_sid CHAR(36),
|
||||
expires_at TIMESTAMP,
|
||||
expires_at TIMESTAMP NULL DEFAULT NULL,
|
||||
last_used TIMESTAMP NULL DEFAULT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (api_key_sid)
|
||||
) ENGINE=InnoDB COMMENT='An authorization token that is used to access the REST api';
|
||||
) COMMENT='An authorization token that is used to access the REST api';
|
||||
|
||||
CREATE TABLE sbc_addresses
|
||||
(
|
||||
sbc_address_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
ipv4 VARCHAR(255) NOT NULL,
|
||||
port INTEGER NOT NULL DEFAULT 5060,
|
||||
service_provider_sid CHAR(36),
|
||||
PRIMARY KEY (sbc_address_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE ms_teams_tenants
|
||||
(
|
||||
@@ -69,60 +205,121 @@ tenant_fqdn VARCHAR(255) NOT NULL UNIQUE ,
|
||||
PRIMARY KEY (ms_teams_tenant_sid)
|
||||
) COMMENT='A Microsoft Teams customer tenant';
|
||||
|
||||
CREATE TABLE sbc_addresses
|
||||
CREATE TABLE signup_history
|
||||
(
|
||||
sbc_address_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255),
|
||||
signed_up_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (email)
|
||||
);
|
||||
|
||||
CREATE TABLE smpp_addresses
|
||||
(
|
||||
smpp_address_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
ipv4 VARCHAR(255) NOT NULL,
|
||||
port INTEGER NOT NULL DEFAULT 5060,
|
||||
use_tls BOOLEAN NOT NULL DEFAULT 0,
|
||||
is_primary BOOLEAN NOT NULL DEFAULT 1,
|
||||
service_provider_sid CHAR(36),
|
||||
PRIMARY KEY (sbc_address_sid)
|
||||
PRIMARY KEY (smpp_address_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE speech_credentials
|
||||
(
|
||||
speech_credential_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
service_provider_sid CHAR(36),
|
||||
account_sid CHAR(36),
|
||||
vendor VARCHAR(32) NOT NULL,
|
||||
credential VARCHAR(8192) NOT NULL,
|
||||
use_for_tts BOOLEAN DEFAULT true,
|
||||
use_for_stt BOOLEAN DEFAULT true,
|
||||
last_used DATETIME,
|
||||
last_tested DATETIME,
|
||||
tts_tested_ok BOOLEAN,
|
||||
stt_tested_ok BOOLEAN,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (speech_credential_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE users
|
||||
(
|
||||
user_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
name CHAR(36) NOT NULL UNIQUE ,
|
||||
hashed_password VARCHAR(1024) NOT NULL,
|
||||
salt CHAR(16) NOT NULL,
|
||||
force_change BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
pending_email VARCHAR(255),
|
||||
phone VARCHAR(20) UNIQUE ,
|
||||
hashed_password VARCHAR(1024),
|
||||
account_sid CHAR(36),
|
||||
service_provider_sid CHAR(36),
|
||||
force_change BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
provider VARCHAR(255) NOT NULL,
|
||||
provider_userid VARCHAR(255),
|
||||
scope VARCHAR(16) NOT NULL DEFAULT 'read-write',
|
||||
phone_activation_code VARCHAR(16),
|
||||
email_activation_code VARCHAR(16),
|
||||
email_validated BOOLEAN NOT NULL DEFAULT false,
|
||||
phone_validated BOOLEAN NOT NULL DEFAULT false,
|
||||
email_content_opt_out BOOLEAN NOT NULL DEFAULT false,
|
||||
PRIMARY KEY (user_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE voip_carriers
|
||||
(
|
||||
voip_carrier_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
name VARCHAR(64) NOT NULL UNIQUE ,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
description VARCHAR(255),
|
||||
account_sid CHAR(36) COMMENT 'if provided, indicates this entity represents a customer PBX that is associated with a specific account',
|
||||
account_sid CHAR(36) COMMENT 'if provided, indicates this entity represents a sip trunk that is associated with a specific account',
|
||||
service_provider_sid CHAR(36),
|
||||
application_sid CHAR(36) COMMENT 'If provided, all incoming calls from this source will be routed to the associated application',
|
||||
e164_leading_plus BOOLEAN NOT NULL DEFAULT false,
|
||||
e164_leading_plus BOOLEAN NOT NULL DEFAULT false COMMENT 'if true, a leading plus should be prepended to outbound phone numbers',
|
||||
requires_register BOOLEAN NOT NULL DEFAULT false,
|
||||
register_username VARCHAR(64),
|
||||
register_sip_realm VARCHAR(64),
|
||||
register_password VARCHAR(64),
|
||||
tech_prefix VARCHAR(16) COMMENT 'tech prefix to prepend to outbound calls to this carrier',
|
||||
inbound_auth_username VARCHAR(64),
|
||||
inbound_auth_password VARCHAR(64),
|
||||
diversion VARCHAR(32),
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
smpp_system_id VARCHAR(255),
|
||||
smpp_password VARCHAR(64),
|
||||
smpp_enquire_link_interval INTEGER DEFAULT 0,
|
||||
smpp_inbound_system_id VARCHAR(255),
|
||||
smpp_inbound_password VARCHAR(64),
|
||||
PRIMARY KEY (voip_carrier_sid)
|
||||
) ENGINE=InnoDB COMMENT='A Carrier or customer PBX that can send or receive calls';
|
||||
) COMMENT='A Carrier or customer PBX that can send or receive calls';
|
||||
|
||||
CREATE TABLE smpp_gateways
|
||||
(
|
||||
smpp_gateway_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
ipv4 VARCHAR(128) NOT NULL,
|
||||
port INTEGER NOT NULL DEFAULT 2775,
|
||||
netmask INTEGER NOT NULL DEFAULT 32,
|
||||
is_primary BOOLEAN NOT NULL DEFAULT 1,
|
||||
inbound BOOLEAN NOT NULL DEFAULT 0 COMMENT 'if true, whitelist this IP to allow inbound calls from the gateway',
|
||||
outbound BOOLEAN NOT NULL DEFAULT 1 COMMENT 'if true, include in least-cost routing when placing calls to the PSTN',
|
||||
use_tls BOOLEAN DEFAULT 0,
|
||||
voip_carrier_sid CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (smpp_gateway_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE phone_numbers
|
||||
(
|
||||
phone_number_sid CHAR(36) UNIQUE ,
|
||||
number VARCHAR(32) NOT NULL UNIQUE ,
|
||||
voip_carrier_sid CHAR(36) NOT NULL,
|
||||
voip_carrier_sid CHAR(36),
|
||||
account_sid CHAR(36),
|
||||
application_sid CHAR(36),
|
||||
service_provider_sid CHAR(36) COMMENT 'if not null, this number is a test number for the associated service provider',
|
||||
PRIMARY KEY (phone_number_sid)
|
||||
) ENGINE=InnoDB COMMENT='A phone number that has been assigned to an account';
|
||||
|
||||
CREATE TABLE webhooks
|
||||
(
|
||||
webhook_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
url VARCHAR(1024) NOT NULL,
|
||||
method ENUM("GET","POST") NOT NULL DEFAULT 'POST',
|
||||
username VARCHAR(255),
|
||||
password VARCHAR(255),
|
||||
PRIMARY KEY (webhook_sid)
|
||||
) COMMENT='An HTTP callback';
|
||||
) COMMENT='A phone number that has been assigned to an account';
|
||||
|
||||
CREATE TABLE sip_gateways
|
||||
(
|
||||
sip_gateway_sid CHAR(36),
|
||||
ipv4 VARCHAR(128) NOT NULL COMMENT 'ip address or DNS name of the gateway. For gateways providing inbound calling service, ip address is required.',
|
||||
netmask INTEGER NOT NULL DEFAULT 32,
|
||||
port INTEGER NOT NULL DEFAULT 5060 COMMENT 'sip signaling port',
|
||||
inbound BOOLEAN NOT NULL COMMENT 'if true, whitelist this IP to allow inbound calls from the gateway',
|
||||
outbound BOOLEAN NOT NULL COMMENT 'if true, include in least-cost routing when placing calls to the PSTN',
|
||||
@@ -141,20 +338,33 @@ priority INTEGER NOT NULL DEFAULT 0 COMMENT 'lower priority carriers are attempt
|
||||
PRIMARY KEY (lcr_carrier_set_entry_sid)
|
||||
) COMMENT='An entry in the LCR routing list';
|
||||
|
||||
CREATE TABLE webhooks
|
||||
(
|
||||
webhook_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
url VARCHAR(1024) NOT NULL,
|
||||
method ENUM("GET","POST") NOT NULL DEFAULT 'POST',
|
||||
username VARCHAR(255),
|
||||
password VARCHAR(255),
|
||||
PRIMARY KEY (webhook_sid)
|
||||
) COMMENT='An HTTP callback';
|
||||
|
||||
CREATE TABLE applications
|
||||
(
|
||||
application_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
account_sid CHAR(36) NOT NULL COMMENT 'account that this application belongs to',
|
||||
call_hook_sid CHAR(36) COMMENT 'webhook to call for inbound calls to phone numbers owned by this account',
|
||||
service_provider_sid CHAR(36) COMMENT 'if non-null, this application is a test application that can be used by any account under the associated service provider',
|
||||
account_sid CHAR(36) COMMENT 'account that this application belongs to (if null, this is a service provider test application)',
|
||||
call_hook_sid CHAR(36) COMMENT 'webhook to call for inbound calls ',
|
||||
call_status_hook_sid CHAR(36) COMMENT 'webhook to call for call status events',
|
||||
messaging_hook_sid CHAR(36) COMMENT 'webhook to call for inbound SMS/MMS ',
|
||||
speech_synthesis_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||
speech_synthesis_language VARCHAR(12) NOT NULL DEFAULT 'en-US',
|
||||
speech_synthesis_voice VARCHAR(64),
|
||||
speech_recognizer_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||
speech_recognizer_language VARCHAR(64) NOT NULL DEFAULT 'en-US',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (application_sid)
|
||||
) ENGINE=InnoDB COMMENT='A defined set of behaviors to be applied to phone calls ';
|
||||
) COMMENT='A defined set of behaviors to be applied to phone calls ';
|
||||
|
||||
CREATE TABLE service_providers
|
||||
(
|
||||
@@ -165,7 +375,7 @@ root_domain VARCHAR(128) UNIQUE ,
|
||||
registration_hook_sid CHAR(36),
|
||||
ms_teams_fqdn VARCHAR(255),
|
||||
PRIMARY KEY (service_provider_sid)
|
||||
) ENGINE=InnoDB COMMENT='A partition of the platform used by one service provider';
|
||||
) COMMENT='A partition of the platform used by one service provider';
|
||||
|
||||
CREATE TABLE accounts
|
||||
(
|
||||
@@ -174,72 +384,151 @@ name VARCHAR(64) NOT NULL,
|
||||
sip_realm VARCHAR(132) UNIQUE COMMENT 'sip domain that will be used for devices registering under this account',
|
||||
service_provider_sid CHAR(36) NOT NULL COMMENT 'service provider that owns the customer relationship with this account',
|
||||
registration_hook_sid CHAR(36) COMMENT 'webhook to call when devices underr this account attempt to register',
|
||||
queue_event_hook_sid CHAR(36),
|
||||
device_calling_application_sid CHAR(36) COMMENT 'application to use for outbound calling from an account',
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
plan_type ENUM('trial','free','paid') NOT NULL DEFAULT 'trial',
|
||||
stripe_customer_id VARCHAR(56),
|
||||
webhook_secret VARCHAR(36) NOT NULL,
|
||||
disable_cdrs BOOLEAN NOT NULL DEFAULT 0,
|
||||
trial_end_date DATETIME,
|
||||
deactivated_reason VARCHAR(255),
|
||||
device_to_call_ratio INTEGER NOT NULL DEFAULT 5,
|
||||
PRIMARY KEY (account_sid)
|
||||
) ENGINE=InnoDB COMMENT='An enterprise that uses the platform for comm services';
|
||||
) COMMENT='An enterprise that uses the platform for comm services';
|
||||
|
||||
CREATE INDEX account_static_ip_sid_idx ON account_static_ips (account_static_ip_sid);
|
||||
CREATE INDEX account_sid_idx ON account_static_ips (account_sid);
|
||||
ALTER TABLE account_static_ips ADD FOREIGN KEY account_sid_idxfk (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX account_subscription_sid_idx ON account_subscriptions (account_subscription_sid);
|
||||
CREATE INDEX account_sid_idx ON account_subscriptions (account_sid);
|
||||
ALTER TABLE account_subscriptions ADD FOREIGN KEY account_sid_idxfk_1 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX invite_code_idx ON beta_invite_codes (invite_code);
|
||||
CREATE INDEX call_route_sid_idx ON call_routes (call_route_sid);
|
||||
ALTER TABLE call_routes ADD FOREIGN KEY account_sid_idxfk (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE call_routes ADD FOREIGN KEY account_sid_idxfk_2 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
ALTER TABLE call_routes ADD FOREIGN KEY application_sid_idxfk (application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
CREATE INDEX dns_record_sid_idx ON dns_records (dns_record_sid);
|
||||
ALTER TABLE dns_records ADD FOREIGN KEY account_sid_idxfk_3 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX predefined_carrier_sid_idx ON predefined_carriers (predefined_carrier_sid);
|
||||
CREATE INDEX predefined_sip_gateway_sid_idx ON predefined_sip_gateways (predefined_sip_gateway_sid);
|
||||
CREATE INDEX predefined_carrier_sid_idx ON predefined_sip_gateways (predefined_carrier_sid);
|
||||
ALTER TABLE predefined_sip_gateways ADD FOREIGN KEY predefined_carrier_sid_idxfk (predefined_carrier_sid) REFERENCES predefined_carriers (predefined_carrier_sid);
|
||||
|
||||
CREATE INDEX product_sid_idx ON products (product_sid);
|
||||
CREATE INDEX account_product_sid_idx ON account_products (account_product_sid);
|
||||
CREATE INDEX account_subscription_sid_idx ON account_products (account_subscription_sid);
|
||||
ALTER TABLE account_products ADD FOREIGN KEY account_subscription_sid_idxfk (account_subscription_sid) REFERENCES account_subscriptions (account_subscription_sid);
|
||||
|
||||
ALTER TABLE account_products ADD FOREIGN KEY product_sid_idxfk (product_sid) REFERENCES products (product_sid);
|
||||
|
||||
CREATE INDEX account_offer_sid_idx ON account_offers (account_offer_sid);
|
||||
CREATE INDEX account_sid_idx ON account_offers (account_sid);
|
||||
ALTER TABLE account_offers ADD FOREIGN KEY account_sid_idxfk_4 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX product_sid_idx ON account_offers (product_sid);
|
||||
ALTER TABLE account_offers ADD FOREIGN KEY product_sid_idxfk_1 (product_sid) REFERENCES products (product_sid);
|
||||
|
||||
CREATE INDEX api_key_sid_idx ON api_keys (api_key_sid);
|
||||
CREATE INDEX account_sid_idx ON api_keys (account_sid);
|
||||
ALTER TABLE api_keys ADD FOREIGN KEY account_sid_idxfk_1 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE api_keys ADD FOREIGN KEY account_sid_idxfk_5 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX service_provider_sid_idx ON api_keys (service_provider_sid);
|
||||
ALTER TABLE api_keys ADD FOREIGN KEY service_provider_sid_idxfk (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE INDEX ms_teams_tenant_sid_idx ON ms_teams_tenants (ms_teams_tenant_sid);
|
||||
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY service_provider_sid_idxfk_1 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY account_sid_idxfk_2 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY application_sid_idxfk_1 (application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
CREATE INDEX tenant_fqdn_idx ON ms_teams_tenants (tenant_fqdn);
|
||||
CREATE INDEX sbc_addresses_idx_host_port ON sbc_addresses (ipv4,port);
|
||||
|
||||
CREATE INDEX sbc_address_sid_idx ON sbc_addresses (sbc_address_sid);
|
||||
CREATE INDEX service_provider_sid_idx ON sbc_addresses (service_provider_sid);
|
||||
ALTER TABLE sbc_addresses ADD FOREIGN KEY service_provider_sid_idxfk_2 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
ALTER TABLE sbc_addresses ADD FOREIGN KEY service_provider_sid_idxfk_1 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE INDEX ms_teams_tenant_sid_idx ON ms_teams_tenants (ms_teams_tenant_sid);
|
||||
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY service_provider_sid_idxfk_2 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY account_sid_idxfk_6 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY application_sid_idxfk_1 (application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
CREATE INDEX tenant_fqdn_idx ON ms_teams_tenants (tenant_fqdn);
|
||||
CREATE INDEX email_idx ON signup_history (email);
|
||||
CREATE INDEX smpp_address_sid_idx ON smpp_addresses (smpp_address_sid);
|
||||
CREATE INDEX service_provider_sid_idx ON smpp_addresses (service_provider_sid);
|
||||
ALTER TABLE smpp_addresses ADD FOREIGN KEY service_provider_sid_idxfk_3 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE UNIQUE INDEX speech_credentials_idx_1 ON speech_credentials (vendor,account_sid);
|
||||
|
||||
CREATE INDEX speech_credential_sid_idx ON speech_credentials (speech_credential_sid);
|
||||
CREATE INDEX service_provider_sid_idx ON speech_credentials (service_provider_sid);
|
||||
ALTER TABLE speech_credentials ADD FOREIGN KEY service_provider_sid_idxfk_4 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE INDEX account_sid_idx ON speech_credentials (account_sid);
|
||||
ALTER TABLE speech_credentials ADD FOREIGN KEY account_sid_idxfk_7 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX user_sid_idx ON users (user_sid);
|
||||
CREATE INDEX name_idx ON users (name);
|
||||
CREATE INDEX email_idx ON users (email);
|
||||
CREATE INDEX phone_idx ON users (phone);
|
||||
CREATE INDEX account_sid_idx ON users (account_sid);
|
||||
ALTER TABLE users ADD FOREIGN KEY account_sid_idxfk_8 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX service_provider_sid_idx ON users (service_provider_sid);
|
||||
ALTER TABLE users ADD FOREIGN KEY service_provider_sid_idxfk_5 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE INDEX email_activation_code_idx ON users (email_activation_code);
|
||||
CREATE INDEX voip_carrier_sid_idx ON voip_carriers (voip_carrier_sid);
|
||||
CREATE INDEX name_idx ON voip_carriers (name);
|
||||
ALTER TABLE voip_carriers ADD FOREIGN KEY account_sid_idxfk_3 (account_sid) REFERENCES accounts (account_sid);
|
||||
CREATE INDEX account_sid_idx ON voip_carriers (account_sid);
|
||||
ALTER TABLE voip_carriers ADD FOREIGN KEY account_sid_idxfk_9 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX service_provider_sid_idx ON voip_carriers (service_provider_sid);
|
||||
ALTER TABLE voip_carriers ADD FOREIGN KEY service_provider_sid_idxfk_6 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
ALTER TABLE voip_carriers ADD FOREIGN KEY application_sid_idxfk_2 (application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
CREATE INDEX phone_number_sid_idx ON phone_numbers (phone_number_sid);
|
||||
CREATE INDEX voip_carrier_sid_idx ON phone_numbers (voip_carrier_sid);
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY voip_carrier_sid_idxfk (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
CREATE INDEX smpp_gateway_sid_idx ON smpp_gateways (smpp_gateway_sid);
|
||||
CREATE INDEX voip_carrier_sid_idx ON smpp_gateways (voip_carrier_sid);
|
||||
ALTER TABLE smpp_gateways ADD FOREIGN KEY voip_carrier_sid_idxfk (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY account_sid_idxfk_4 (account_sid) REFERENCES accounts (account_sid);
|
||||
CREATE INDEX phone_number_sid_idx ON phone_numbers (phone_number_sid);
|
||||
CREATE INDEX number_idx ON phone_numbers (number);
|
||||
CREATE INDEX voip_carrier_sid_idx ON phone_numbers (voip_carrier_sid);
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY voip_carrier_sid_idxfk_1 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY account_sid_idxfk_10 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY application_sid_idxfk_3 (application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
CREATE INDEX webhook_sid_idx ON webhooks (webhook_sid);
|
||||
CREATE UNIQUE INDEX sip_gateway_idx_hostport ON sip_gateways (ipv4,port);
|
||||
CREATE INDEX service_provider_sid_idx ON phone_numbers (service_provider_sid);
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY service_provider_sid_idxfk_7 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
ALTER TABLE sip_gateways ADD FOREIGN KEY voip_carrier_sid_idxfk_1 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
CREATE INDEX sip_gateway_idx_hostport ON sip_gateways (ipv4,port);
|
||||
|
||||
CREATE INDEX voip_carrier_sid_idx ON sip_gateways (voip_carrier_sid);
|
||||
ALTER TABLE sip_gateways ADD FOREIGN KEY voip_carrier_sid_idxfk_2 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
|
||||
ALTER TABLE lcr_carrier_set_entry ADD FOREIGN KEY lcr_route_sid_idxfk (lcr_route_sid) REFERENCES lcr_routes (lcr_route_sid);
|
||||
|
||||
ALTER TABLE lcr_carrier_set_entry ADD FOREIGN KEY voip_carrier_sid_idxfk_2 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
ALTER TABLE lcr_carrier_set_entry ADD FOREIGN KEY voip_carrier_sid_idxfk_3 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
|
||||
CREATE INDEX webhook_sid_idx ON webhooks (webhook_sid);
|
||||
CREATE UNIQUE INDEX applications_idx_name ON applications (account_sid,name);
|
||||
|
||||
CREATE INDEX application_sid_idx ON applications (application_sid);
|
||||
CREATE INDEX service_provider_sid_idx ON applications (service_provider_sid);
|
||||
ALTER TABLE applications ADD FOREIGN KEY service_provider_sid_idxfk_8 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE INDEX account_sid_idx ON applications (account_sid);
|
||||
ALTER TABLE applications ADD FOREIGN KEY account_sid_idxfk_5 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE applications ADD FOREIGN KEY account_sid_idxfk_11 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
ALTER TABLE applications ADD FOREIGN KEY call_hook_sid_idxfk (call_hook_sid) REFERENCES webhooks (webhook_sid);
|
||||
|
||||
ALTER TABLE applications ADD FOREIGN KEY call_status_hook_sid_idxfk (call_status_hook_sid) REFERENCES webhooks (webhook_sid);
|
||||
|
||||
ALTER TABLE applications ADD FOREIGN KEY messaging_hook_sid_idxfk (messaging_hook_sid) REFERENCES webhooks (webhook_sid);
|
||||
|
||||
CREATE INDEX service_provider_sid_idx ON service_providers (service_provider_sid);
|
||||
CREATE INDEX name_idx ON service_providers (name);
|
||||
CREATE INDEX root_domain_idx ON service_providers (root_domain);
|
||||
@@ -248,10 +537,12 @@ ALTER TABLE service_providers ADD FOREIGN KEY registration_hook_sid_idxfk (regis
|
||||
CREATE INDEX account_sid_idx ON accounts (account_sid);
|
||||
CREATE INDEX sip_realm_idx ON accounts (sip_realm);
|
||||
CREATE INDEX service_provider_sid_idx ON accounts (service_provider_sid);
|
||||
ALTER TABLE accounts ADD FOREIGN KEY service_provider_sid_idxfk_3 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
ALTER TABLE accounts ADD FOREIGN KEY service_provider_sid_idxfk_9 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
ALTER TABLE accounts ADD FOREIGN KEY registration_hook_sid_idxfk_1 (registration_hook_sid) REFERENCES webhooks (webhook_sid);
|
||||
|
||||
ALTER TABLE accounts ADD FOREIGN KEY queue_event_hook_sid_idxfk (queue_event_hook_sid) REFERENCES webhooks (webhook_sid);
|
||||
|
||||
ALTER TABLE accounts ADD FOREIGN KEY device_calling_application_sid_idxfk (device_calling_application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
SET FOREIGN_KEY_CHECKS=1;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
DROP DATABASE jambones_test;
|
||||
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'jambones_test'@'localhost';
|
||||
DROP USER 'jambones_test'@'localhost';
|
||||
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'jambones_test'@'%';
|
||||
DROP USER 'jambones_test'@'%';
|
||||
|
||||
@@ -1,55 +1,131 @@
|
||||
version: '3'
|
||||
|
||||
version: '3.9'
|
||||
networks:
|
||||
sbc-inbound:
|
||||
fs:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.38.0.0/16
|
||||
|
||||
services:
|
||||
sbc:
|
||||
image: drachtio/drachtio-server:latest
|
||||
command: drachtio --contact "sip:*;transport=udp" --loglevel debug --sofia-loglevel 9
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
ports:
|
||||
- "9060:9022/tcp"
|
||||
- "3360:3306"
|
||||
environment:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin" ,"ping", "-h", "127.0.0.1", "--protocol", "tcp"]
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
sbc-inbound:
|
||||
ipv4_address: 172.38.0.10
|
||||
|
||||
appserver:
|
||||
image: drachtio/sipp:latest
|
||||
command: sipp -sf /tmp/uas.xml
|
||||
volumes:
|
||||
- ./scenarios:/tmp
|
||||
tty: true
|
||||
networks:
|
||||
sbc-inbound:
|
||||
ipv4_address: 172.38.0.11
|
||||
|
||||
auth-server:
|
||||
image: jambonz/customer-auth-server:latest
|
||||
command: npm start
|
||||
ports:
|
||||
- "4000:4000/tcp"
|
||||
env_file: docker.env
|
||||
networks:
|
||||
sbc-inbound:
|
||||
ipv4_address: 172.38.0.12
|
||||
fs:
|
||||
ipv4_address: 172.38.0.5
|
||||
|
||||
redis:
|
||||
image: redis:5-alpine
|
||||
ports:
|
||||
- "16379:6379/tcp"
|
||||
depends_on:
|
||||
- mysql
|
||||
networks:
|
||||
sbc-inbound:
|
||||
ipv4_address: 172.38.0.13
|
||||
fs:
|
||||
ipv4_address: 172.38.0.6
|
||||
|
||||
rtpengine:
|
||||
image: drachtio/rtpengine:latest
|
||||
docker-host:
|
||||
image: qoomon/docker-host
|
||||
cap_add: [ 'NET_ADMIN', 'NET_RAW' ]
|
||||
mem_limit: 8M
|
||||
restart: on-failure
|
||||
networks:
|
||||
fs:
|
||||
ipv4_address: 172.38.0.7
|
||||
|
||||
drachtio:
|
||||
image: drachtio/drachtio-server:latest
|
||||
restart: always
|
||||
command: drachtio --contact "sip:*;transport=udp,tcp" --address 0.0.0.0 --port 9022
|
||||
ports:
|
||||
- "12222:22222/udp"
|
||||
- "9060:9022/tcp"
|
||||
networks:
|
||||
sbc-inbound:
|
||||
ipv4_address: 172.38.0.14
|
||||
fs:
|
||||
ipv4_address: 172.38.0.50
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
freeswitch:
|
||||
condition: service_healthy
|
||||
|
||||
freeswitch:
|
||||
image: drachtio/drachtio-freeswitch-mrf:v1.10.1-full
|
||||
restart: always
|
||||
command: freeswitch --rtp-range-start 20000 --rtp-range-end 20100
|
||||
environment:
|
||||
GOOGLE_APPLICATION_CREDENTIALS: /opt/credentials/gcp.json
|
||||
ports:
|
||||
- "8022:8021/tcp"
|
||||
volumes:
|
||||
- /tmp:/tmp
|
||||
- ./credentials:/opt/credentials
|
||||
healthcheck:
|
||||
test: ['CMD', 'fs_cli' ,'-x', '"sofia status"']
|
||||
timeout: 5s
|
||||
retries: 15
|
||||
networks:
|
||||
fs:
|
||||
ipv4_address: 172.38.0.51
|
||||
|
||||
webhook-decline:
|
||||
image: jambonz/webhook-test-scaffold:latest
|
||||
environment:
|
||||
APP_PATH: /tmp/decline.json
|
||||
ports:
|
||||
- "3100:3000/tcp"
|
||||
volumes:
|
||||
- ./test-apps:/tmp
|
||||
networks:
|
||||
fs:
|
||||
ipv4_address: 172.38.0.60
|
||||
|
||||
webhook-say:
|
||||
image: jambonz/webhook-test-scaffold:latest
|
||||
environment:
|
||||
APP_PATH: /tmp/say.json
|
||||
ports:
|
||||
- "3101:3000/tcp"
|
||||
volumes:
|
||||
- ./test-apps:/tmp
|
||||
networks:
|
||||
fs:
|
||||
ipv4_address: 172.38.0.61
|
||||
|
||||
webhook-gather:
|
||||
image: jambonz/webhook-test-scaffold:latest
|
||||
environment:
|
||||
APP_PATH: /tmp/gather.json
|
||||
ports:
|
||||
- "3102:3000/tcp"
|
||||
volumes:
|
||||
- ./test-apps:/tmp
|
||||
networks:
|
||||
fs:
|
||||
ipv4_address: 172.38.0.62
|
||||
|
||||
webhook-transcribe:
|
||||
image: jambonz/webhook-test-scaffold:latest
|
||||
environment:
|
||||
APP_PATH: /tmp/transcribe.json
|
||||
ports:
|
||||
- "3103:3000/tcp"
|
||||
volumes:
|
||||
- ./test-apps:/tmp
|
||||
networks:
|
||||
fs:
|
||||
ipv4_address: 172.38.0.63
|
||||
|
||||
influxdb:
|
||||
image: influxdb:1.8-alpine
|
||||
ports:
|
||||
- "8086:8086"
|
||||
networks:
|
||||
fs:
|
||||
ipv4_address: 172.38.0.90
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
const test = require('tape').test ;
|
||||
const test = require('tape') ;
|
||||
const exec = require('child_process').exec ;
|
||||
const async = require('async');
|
||||
|
||||
test('starting docker network..', (t) => {
|
||||
test('starting docker network..takes a bit for mysql and freeswitch to come up..patience..', (t) => {
|
||||
exec(`docker-compose -f ${__dirname}/docker-compose-testbed.yaml up -d`, (err, stdout, stderr) => {
|
||||
t.pass('docker network is up');
|
||||
t.end(err);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('tape').test ;
|
||||
const test = require('tape') ;
|
||||
const exec = require('child_process').exec ;
|
||||
|
||||
test('stopping docker network..', (t) => {
|
||||
|
||||
36
test/gather-tests.js
Normal file
36
test/gather-tests.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const test = require('tape');
|
||||
const { sippUac } = require('./sipp')('test_fs');
|
||||
const bent = require('bent');
|
||||
const getJSON = bent('json')
|
||||
const clearModule = require('clear-module');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
function connect(connectable) {
|
||||
return new Promise((resolve, reject) => {
|
||||
connectable.on('connect', () => {
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('\'gather\' and \'transcribe\' tests', async(t) => {
|
||||
clearModule.all();
|
||||
const {srf, disconnect} = require('../app');
|
||||
|
||||
try {
|
||||
await connect(srf);
|
||||
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10');
|
||||
let obj = await getJSON('http://127.0.0.1:3102/actionHook');
|
||||
t.ok(obj.speech.alternatives[0].transcript = 'I\'d like to speak to customer support',
|
||||
'gather: succeeds when using account credentials');
|
||||
|
||||
disconnect();
|
||||
} catch (err) {
|
||||
console.log(`error received: ${err}`);
|
||||
disconnect();
|
||||
t.error(err);
|
||||
}
|
||||
});
|
||||
@@ -1,8 +1,9 @@
|
||||
require('./unit-tests');
|
||||
/*
|
||||
require('./docker_start');
|
||||
require('./create-test-db');
|
||||
require('./sip-tests');
|
||||
require('./account-validation-tests');
|
||||
require('./webhooks-tests');
|
||||
require('./say-tests');
|
||||
require('./gather-tests');
|
||||
require('./remove-test-db');
|
||||
require('./docker_stop');
|
||||
*/
|
||||
@@ -1,11 +1,11 @@
|
||||
const test = require('tape').test ;
|
||||
const test = require('tape') ;
|
||||
const exec = require('child_process').exec ;
|
||||
const pwd = process.env.TRAVIS ? '' : '-p$MYSQL_ROOT_PASSWORD';
|
||||
|
||||
const fs = require('fs');
|
||||
test('dropping jambones_test database', (t) => {
|
||||
exec(`mysql -h localhost -u root ${pwd} < ${__dirname}/db/remove_test_db.sql`, (err, stdout, stderr) => {
|
||||
exec(`mysql -h 127.0.0.1 -u root --protocol=tcp --port=3360 < ${__dirname}/db/remove_test_db.sql`, (err, stdout, stderr) => {
|
||||
if (err) return t.end(err);
|
||||
t.pass('database successfully dropped');
|
||||
fs.unlinkSync(`${__dirname}/credentials/gcp.json`);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
32
test/say-tests.js
Normal file
32
test/say-tests.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const test = require('tape');
|
||||
const { sippUac } = require('./sipp')('test_fs');
|
||||
const clearModule = require('clear-module');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
function connect(connectable) {
|
||||
return new Promise((resolve, reject) => {
|
||||
connectable.on('connect', () => {
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('\'say\' tests', async(t) => {
|
||||
clearModule.all();
|
||||
const {srf, disconnect} = require('../app');
|
||||
|
||||
try {
|
||||
await connect(srf);
|
||||
await sippUac('uac-say-account-creds-success.xml', '172.38.0.10');
|
||||
t.pass('say: succeeds when using using account credentials');
|
||||
|
||||
disconnect();
|
||||
} catch (err) {
|
||||
console.log(`error received: ${err}`);
|
||||
disconnect();
|
||||
t.error(err);
|
||||
}
|
||||
});
|
||||
55
test/scenarios/uac-expect-500.xml
Normal file
55
test/scenarios/uac-expect-500.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-expect-500
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="500">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
|
||||
[last_Via]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: [service] <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Max-Forwards: 70
|
||||
Subject: uac-expect-500
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
</scenario>
|
||||
|
||||
56
test/scenarios/uac-expect-603.xml
Normal file
56
test/scenarios/uac-expect-603.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16174000000@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000000@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-expect-603
|
||||
X-Account-Sid: bb845d4b-83a9-4cde-a6e9-50f3743bab3f
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="603">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:16174000000@[remote_ip]:[remote_port] SIP/2.0
|
||||
[last_Via]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000000@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Max-Forwards: 70
|
||||
Subject: uac-expect-603
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
</scenario>
|
||||
|
||||
95
test/scenarios/uac-gather-account-creds-success.xml
Normal file
95
test/scenarios/uac-gather-account-creds-success.xml
Normal file
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16174000003@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000003@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: bb845d4b-83a9-4cde-a6e9-50f3743bab3f
|
||||
Subject: uac-gather-account-creds-success
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="183" optional="true">
|
||||
</recv>
|
||||
|
||||
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
|
||||
<!-- are saved and used for following messages sent. Useful to test -->
|
||||
<!-- against stateful SIP proxies/B2BUAs. -->
|
||||
<recv response="200" rtd="true">
|
||||
</recv>
|
||||
|
||||
<!-- Packet lost can be simulated in any send/recv message by -->
|
||||
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:16174000003@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: 16174000003 <sip:16174000003@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-gather-account-creds-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<nop>
|
||||
<action>
|
||||
<exec rtp_stream="/tmp/scenarios/wav/speak-to-customer-support.wav,1,0"/>
|
||||
</action>
|
||||
</nop>
|
||||
|
||||
<recv request="BYE">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
SIP/2.0 200 OK
|
||||
[last_Via:]
|
||||
[last_From:]
|
||||
[last_To:]
|
||||
[last_Call-ID:]
|
||||
[last_CSeq:]
|
||||
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
</scenario>
|
||||
|
||||
56
test/scenarios/uac-inactive-account-expect-503.xml
Normal file
56
test/scenarios/uac-inactive-account-expect-503.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000001@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: 2a14b9ef-2d01-4b50-8123-fa79f56ab684
|
||||
Subject: uac-inactive-account-expect-503
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="503">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
|
||||
[last_Via]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000001@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Max-Forwards: 70
|
||||
Subject: uac-inactive-account-expect-503
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
</scenario>
|
||||
|
||||
56
test/scenarios/uac-invalid-account-expect-503.xml
Normal file
56
test/scenarios/uac-invalid-account-expect-503.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: deadbeef
|
||||
Subject: uac-invalid-account-expect-503
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="503">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
|
||||
[last_Via]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: [service] <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Max-Forwards: 70
|
||||
Subject: uac-invalid-account-expect-503
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
</scenario>
|
||||
|
||||
89
test/scenarios/uac-say-account-creds-success.xml
Normal file
89
test/scenarios/uac-say-account-creds-success.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16174000001@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000001@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: bb845d4b-83a9-4cde-a6e9-50f3743bab3f
|
||||
Subject: uac-say
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="183" optional="true">
|
||||
</recv>
|
||||
|
||||
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
|
||||
<!-- are saved and used for following messages sent. Useful to test -->
|
||||
<!-- against stateful SIP proxies/B2BUAs. -->
|
||||
<recv response="200" rtd="true">
|
||||
</recv>
|
||||
|
||||
<!-- Packet lost can be simulated in any send/recv message by -->
|
||||
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:16174000001@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: 16174000001 <sip:16174000001@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-say
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv request="BYE">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
SIP/2.0 200 OK
|
||||
[last_Via:]
|
||||
[last_From:]
|
||||
[last_To:]
|
||||
[last_Call-ID:]
|
||||
[last_CSeq:]
|
||||
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
</scenario>
|
||||
|
||||
89
test/scenarios/uac-say-fail-low-balance.xml
Normal file
89
test/scenarios/uac-say-fail-low-balance.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16174000002@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000002@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: 2a14b9ef-2d01-4b50-8123-fa79f56ab684
|
||||
Subject: uac-say
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="183" optional="true">
|
||||
</recv>
|
||||
|
||||
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
|
||||
<!-- are saved and used for following messages sent. Useful to test -->
|
||||
<!-- against stateful SIP proxies/B2BUAs. -->
|
||||
<recv response="200" rtd="true">
|
||||
</recv>
|
||||
|
||||
<!-- Packet lost can be simulated in any send/recv message by -->
|
||||
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:16174000002@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: 16174000002 <sip:16174000002@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-say
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv request="BYE">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
SIP/2.0 200 OK
|
||||
[last_Via:]
|
||||
[last_From:]
|
||||
[last_To:]
|
||||
[last_Call-ID:]
|
||||
[last_CSeq:]
|
||||
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
</scenario>
|
||||
|
||||
89
test/scenarios/uac-say-our-creds-success.xml
Normal file
89
test/scenarios/uac-say-our-creds-success.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16174000002@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000002@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: 2a14b9ef-2d01-4b50-8123-fa79f56ab684
|
||||
Subject: uac-say
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="183" optional="true">
|
||||
</recv>
|
||||
|
||||
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
|
||||
<!-- are saved and used for following messages sent. Useful to test -->
|
||||
<!-- against stateful SIP proxies/B2BUAs. -->
|
||||
<recv response="200" rtd="true">
|
||||
</recv>
|
||||
|
||||
<!-- Packet lost can be simulated in any send/recv message by -->
|
||||
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:16174000002@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: 16174000002 <sip:16174000002@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-say
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv request="BYE">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
SIP/2.0 200 OK
|
||||
[last_Via:]
|
||||
[last_From:]
|
||||
[last_To:]
|
||||
[last_Call-ID:]
|
||||
[last_CSeq:]
|
||||
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
</scenario>
|
||||
|
||||
99
test/scenarios/uac-transcribe-account-creds-success.xml
Normal file
99
test/scenarios/uac-transcribe-account-creds-success.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16174000004@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000004@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: bb845d4b-83a9-4cde-a6e9-50f3743bab3f
|
||||
Subject: uac-transcribe-account-creds-success
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="183" optional="true">
|
||||
</recv>
|
||||
|
||||
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
|
||||
<!-- are saved and used for following messages sent. Useful to test -->
|
||||
<!-- against stateful SIP proxies/B2BUAs. -->
|
||||
<recv response="200" rtd="true">
|
||||
</recv>
|
||||
|
||||
<!-- Packet lost can be simulated in any send/recv message by -->
|
||||
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:16174000004@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: 16174000004 <sip:16174000004@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-transcribe-account-creds-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<nop>
|
||||
<action>
|
||||
<exec rtp_stream="/tmp/scenarios/wav/speak-to-customer-support.wav,1,0"/>
|
||||
</action>
|
||||
</nop>
|
||||
|
||||
<pause milliseconds="10000"/>
|
||||
|
||||
<!-- The 'crlf' option inserts a blank line in the statistics report. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
BYE sip:sip:16174000004@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: 16174000004 <sip:16174000004@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 2 BYE
|
||||
Max-Forwards: 70
|
||||
Subject: uac-transcribe-account-creds-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200" crlf="true">
|
||||
</recv>
|
||||
|
||||
</scenario>
|
||||
|
||||
88
test/scenarios/uac-transcribe-our-creds-fail-low-balance.xml
Normal file
88
test/scenarios/uac-transcribe-our-creds-fail-low-balance.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16174000005@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000005@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: 2a14b9ef-2d01-4b50-8123-fa79f56ab684
|
||||
Subject: uac-transcribe-our-creds-fail-low-balance
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="183" optional="true">
|
||||
</recv>
|
||||
|
||||
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
|
||||
<!-- are saved and used for following messages sent. Useful to test -->
|
||||
<!-- against stateful SIP proxies/B2BUAs. -->
|
||||
<recv response="200" rtd="true">
|
||||
</recv>
|
||||
|
||||
<!-- Packet lost can be simulated in any send/recv message by -->
|
||||
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:16174000005@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: 16174000005 <sip:16174000004@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-transcribe-our-creds-fail-low-balance
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv request="BYE">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
SIP/2.0 200 OK
|
||||
[last_Via:]
|
||||
[last_From:]
|
||||
[last_To:]
|
||||
[last_Call-ID:]
|
||||
[last_CSeq:]
|
||||
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
|
||||
</scenario>
|
||||
|
||||
99
test/scenarios/uac-transcribe-our-creds-success.xml
Normal file
99
test/scenarios/uac-transcribe-our-creds-success.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
|
||||
<scenario name="Basic Sipstone UAC">
|
||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16174000005@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: <sip:16174000005@[remote_ip]:[remote_port]>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: 2a14b9ef-2d01-4b50-8123-fa79f56ab684
|
||||
Subject: uac-transcribe-our-creds-success
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[media_ip_type] [media_ip]
|
||||
t=0 0
|
||||
m=audio [media_port] RTP/AVP 0
|
||||
a=rtpmap:0 PCMU/8000
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100"
|
||||
optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="183" optional="true">
|
||||
</recv>
|
||||
|
||||
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
|
||||
<!-- are saved and used for following messages sent. Useful to test -->
|
||||
<!-- against stateful SIP proxies/B2BUAs. -->
|
||||
<recv response="200" rtd="true">
|
||||
</recv>
|
||||
|
||||
<!-- Packet lost can be simulated in any send/recv message by -->
|
||||
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:16174000005@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: 16174000005 <sip:16174000004@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-transcribe-our-creds-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<nop>
|
||||
<action>
|
||||
<exec rtp_stream="/tmp/scenarios/wav/speak-to-customer-support.wav,1,0"/>
|
||||
</action>
|
||||
</nop>
|
||||
|
||||
<pause milliseconds="10000"/>
|
||||
|
||||
<!-- The 'crlf' option inserts a blank line in the statistics report. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
BYE sip:sip:16174000005@[remote_ip]:[remote_port] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
|
||||
To: 16174000005 <sip:16174000005@[remote_ip]:[remote_port]>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 2 BYE
|
||||
Max-Forwards: 70
|
||||
Subject: uac-transcribe-our-creds-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200" crlf="true">
|
||||
</recv>
|
||||
|
||||
</scenario>
|
||||
|
||||
BIN
test/scenarios/wav/speak-to-customer-support.wav
Normal file
BIN
test/scenarios/wav/speak-to-customer-support.wav
Normal file
Binary file not shown.
10
test/sipp.js
10
test/sipp.js
@@ -27,19 +27,19 @@ obj.output = () => {
|
||||
obj.sippUac = (file, bindAddress) => {
|
||||
const cmd = 'docker';
|
||||
const args = [
|
||||
'run', '-ti', '--rm', '--net', `${network}`,
|
||||
'run', '-t', '--rm', '--net', `${network}`,
|
||||
'-v', `${__dirname}/scenarios:/tmp/scenarios`,
|
||||
'drachtio/sipp', 'sipp', '-sf', `/tmp/scenarios/${file}`,
|
||||
'-m', '1',
|
||||
'-sleep', '250ms',
|
||||
'-nostdin',
|
||||
'-cid_str', `%u-%p@%s-${idx++}`,
|
||||
'sbc'
|
||||
'172.38.0.50'
|
||||
];
|
||||
|
||||
if (bindAddress) args.splice(5, 0, '--ip', bindAddress);
|
||||
|
||||
//console.log(args.join(' '));
|
||||
console.log(args.join(' '));
|
||||
clearOutput();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -57,11 +57,11 @@ obj.sippUac = (file, bindAddress) => {
|
||||
});
|
||||
|
||||
child_process.stdout.on('data', (data) => {
|
||||
//debug(`stdout: ${data}`);
|
||||
//console.log(`stdout: ${data}`);
|
||||
addOutput(data.toString());
|
||||
});
|
||||
child_process.stdout.on('data', (data) => {
|
||||
//debug(`stdout: ${data}`);
|
||||
//console.log(`stdout: ${data}`);
|
||||
addOutput(data.toString());
|
||||
});
|
||||
});
|
||||
|
||||
6
test/test-apps/decline.json
Normal file
6
test/test-apps/decline.json
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"verb": "sip:decline",
|
||||
"status": 603
|
||||
}
|
||||
]
|
||||
12
test/test-apps/gather.json
Normal file
12
test/test-apps/gather.json
Normal file
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"verb": "gather",
|
||||
"input": ["speech"],
|
||||
"recognizer": {
|
||||
"vendor": "google",
|
||||
"hints": ["customer support", "sales", "human resources", "HR"]
|
||||
},
|
||||
"timeout": 10,
|
||||
"actionHook": "/actionHook"
|
||||
}
|
||||
]
|
||||
6
test/test-apps/say.json
Normal file
6
test/test-apps/say.json
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"verb": "say",
|
||||
"text": "hello"
|
||||
}
|
||||
]
|
||||
9
test/test-apps/transcribe.json
Normal file
9
test/test-apps/transcribe.json
Normal file
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"verb": "transcribe",
|
||||
"recognizer": {
|
||||
"vendor": "google"
|
||||
},
|
||||
"transcriptionHook": "/actionHook"
|
||||
}
|
||||
]
|
||||
@@ -9,7 +9,7 @@ process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
test('app payload parsing tests', (t) => {
|
||||
test('unit tests', (t) => {
|
||||
let task = makeTask(logger, require('./data/good/sip-decline'));
|
||||
t.ok(task.name === 'sip:decline', 'parsed sip:decline');
|
||||
|
||||
@@ -53,7 +53,6 @@ test('app payload parsing tests', (t) => {
|
||||
t.pass('alternate syntax works');
|
||||
|
||||
t.end();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
|
||||
|
||||
15
test/webhook/Dockerfile
Normal file
15
test/webhook/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM node:alpine as builder
|
||||
RUN apk update && apk add --no-cache python make g++
|
||||
WORKDIR /opt/app/
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
RUN npm prune
|
||||
|
||||
FROM node:alpine as webapp
|
||||
RUN apk add curl
|
||||
WORKDIR /opt/app
|
||||
COPY . /opt/app
|
||||
COPY --from=builder /opt/app/node_modules ./node_modules
|
||||
COPY ./entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
49
test/webhook/app.js
Normal file
49
test/webhook/app.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const listenPort = process.env.HTTP_PORT || 3000;
|
||||
let lastAction, lastEvent;
|
||||
|
||||
assert.ok(process.env.APP_PATH, 'env var APP_PATH is required');
|
||||
|
||||
app.listen(listenPort, () => {
|
||||
console.log(`sample jambones app server listening on ${listenPort}`);
|
||||
});
|
||||
|
||||
const applicationData = JSON.parse(fs.readFileSync(process.env.APP_PATH));
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(express.json());
|
||||
|
||||
|
||||
app.all('/', (req, res) => {
|
||||
console.log(applicationData, `${req.method} /`);
|
||||
return res.json(applicationData);
|
||||
});
|
||||
|
||||
app.post('/callStatus', (req, res) => {
|
||||
console.log({payload: req.body}, 'POST /callStatus');
|
||||
return res.sendStatus(200);
|
||||
});
|
||||
|
||||
app.post('/actionHook', (req, res) => {
|
||||
console.log({payload: req.body}, 'POST /actionHook');
|
||||
lastAction = req.body;
|
||||
return res.sendStatus(200);
|
||||
});
|
||||
|
||||
app.get('/actionHook', (req, res) => {
|
||||
console.log({payload: lastAction}, 'GET /actionHook');
|
||||
return res.json(lastAction);
|
||||
});
|
||||
|
||||
app.post('/eventHook', (req, res) => {
|
||||
console.log({payload: req.body}, 'POST /eventHook');
|
||||
lastEvent = req.body;
|
||||
return res.sendStatus(200);
|
||||
});
|
||||
|
||||
app.get('/eventHook', (req, res) => {
|
||||
console.log({payload: lastEvent}, 'GET /eventHook');
|
||||
return res.json(lastEvent);
|
||||
});
|
||||
3
test/webhook/entrypoint.sh
Normal file
3
test/webhook/entrypoint.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd /opt/app/
|
||||
npm start
|
||||
374
test/webhook/package-lock.json
generated
Normal file
374
test/webhook/package-lock.json
generated
Normal file
@@ -0,0 +1,374 @@
|
||||
{
|
||||
"name": "webhook",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"accepts": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||
"requires": {
|
||||
"mime-types": "~2.1.24",
|
||||
"negotiator": "0.6.2"
|
||||
}
|
||||
},
|
||||
"array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"body-parser": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
||||
"requires": {
|
||||
"bytes": "3.1.0",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"http-errors": "1.7.2",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "~2.3.0",
|
||||
"qs": "6.7.0",
|
||||
"raw-body": "2.4.0",
|
||||
"type-is": "~1.6.17"
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
||||
},
|
||||
"content-disposition": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
||||
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
},
|
||||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||
},
|
||||
"destroy": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||
},
|
||||
"encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||
},
|
||||
"etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||
},
|
||||
"express": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
||||
"requires": {
|
||||
"accepts": "~1.3.7",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.19.0",
|
||||
"content-disposition": "0.5.3",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "~1.1.2",
|
||||
"fresh": "0.5.2",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~2.0.5",
|
||||
"qs": "6.7.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.1.2",
|
||||
"send": "0.17.1",
|
||||
"serve-static": "1.14.1",
|
||||
"setprototypeof": "1.1.1",
|
||||
"statuses": "~1.5.0",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
}
|
||||
},
|
||||
"finalhandler": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "~1.5.0",
|
||||
"unpipe": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||
},
|
||||
"fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
||||
"requires": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.3",
|
||||
"setprototypeof": "1.1.1",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.0"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||
},
|
||||
"merge-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||
},
|
||||
"methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.45.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
|
||||
"integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.28",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz",
|
||||
"integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==",
|
||||
"requires": {
|
||||
"mime-db": "1.45.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||
"requires": {
|
||||
"ee-first": "1.1.1"
|
||||
}
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||
"requires": {
|
||||
"forwarded": "~0.1.2",
|
||||
"ipaddr.js": "1.9.1"
|
||||
}
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||
},
|
||||
"range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
||||
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
|
||||
"requires": {
|
||||
"bytes": "3.1.0",
|
||||
"http-errors": "1.7.2",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"send": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
|
||||
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"destroy": "~1.0.4",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "~1.7.2",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.1",
|
||||
"on-finished": "~2.3.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "~1.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve-static": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
|
||||
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
|
||||
"requires": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.17.1"
|
||||
}
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
||||
},
|
||||
"toidentifier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"requires": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
}
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
||||
},
|
||||
"utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||
}
|
||||
}
|
||||
}
|
||||
14
test/webhook/package.json
Normal file
14
test/webhook/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "webhook",
|
||||
"version": "1.0.0",
|
||||
"description": "simple webhook app for test purposes",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "node app"
|
||||
},
|
||||
"author": "Dave Horton",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.17.1"
|
||||
}
|
||||
}
|
||||
32
test/webhooks-tests.js
Normal file
32
test/webhooks-tests.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const test = require('tape');
|
||||
const { sippUac } = require('./sipp')('test_fs');
|
||||
const clearModule = require('clear-module');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
function connect(connectable) {
|
||||
return new Promise((resolve, reject) => {
|
||||
connectable.on('connect', () => {
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('basic webhook tests', async(t) => {
|
||||
clearModule.all();
|
||||
const {srf, disconnect} = require('../app');
|
||||
|
||||
try {
|
||||
await connect(srf);
|
||||
await sippUac('uac-expect-603.xml', '172.38.0.10');
|
||||
t.pass('webhook successfully declines call');
|
||||
|
||||
disconnect();
|
||||
} catch (err) {
|
||||
console.log(`error received: ${err}`);
|
||||
disconnect();
|
||||
t.error(err);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user