added initial support for REST-initiated outdials

This commit is contained in:
Dave Horton
2020-02-01 16:16:00 -05:00
parent 44a1b45357
commit 2525b8c70a
28 changed files with 985 additions and 127 deletions

View File

@@ -8,6 +8,7 @@ const ConfirmCallSession = require('../session/confirm-call-session');
const hooks = require('./notifiers');
const moment = require('moment');
const parseUrl = require('parse-url');
const uuidv4 = require('uuid/v4');
class SingleDialer extends Emitter {
constructor({logger, sbcAddress, target, opts, application, callInfo}) {
@@ -25,23 +26,13 @@ class SingleDialer extends Emitter {
this.bindings = logger.bindings();
this.parentCallInfo = callInfo;
/*
this.callInfo = Object.assign({}, callInfo, {
callSid: this._callSid,
parentCallSid: callInfo.callSid,
direction: CallDirection.Outbound,
callStatus: CallStatus.Trying,
sipStatus: 100
});
*/
this.callGone = false;
this.callSid = uuidv4();
this.on('callStatusChange', this._notifyCallStatusChange.bind(this));
}
get callSid() {
return this._callSid;
}
get callStatus() {
return this.callInfo.callStatus;
}
@@ -88,21 +79,26 @@ class SingleDialer extends Emitter {
if (this.target.auth) opts.auth = this.target.auth;
this.dlg = await srf.createUAC(uri, opts, {
cbRequest: (err, req) => {
if (err) return this.logger.error(err, 'SingleDialer:exec Error creating call');
if (err) {
this.logger.error(err, 'SingleDialer:exec Error creating call');
this.emit('callCreateFail', err);
return;
}
/**
* INVITE has been sent out
* (a) create a CallInfo for this call
* (a) create a logger for this call
* (b) augment this.callInfo with additional call info
*/
this.logger.debug(`call sent, creating CallInfo parentCallInfo is CallInfo? ${this.parentCallInfo instanceof CallInfo}`);
this.callInfo = new CallInfo({
direction: CallDirection.Outbound,
parentCallInfo: this.parentCallInfo,
req
req,
to,
callSid: this.callSid
});
this.logger = srf.locals.parentLogger.child({
callSid: this.callInfo.callSid,
callSid: this.callSid,
parentCallSid: this.parentCallInfo.callSid,
callId: this.callInfo.callId
});
@@ -164,6 +160,7 @@ class SingleDialer extends Emitter {
const duration = moment().diff(this.dlg.connectTime, 'seconds');
this.logger.debug('SingleDialer:kill hanging up called party');
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration});
this.dlg.destroy();
}
if (this.ep) {
this.logger.debug(`SingleDialer:kill - deleting endpoint ${this.ep.uuid}`);
@@ -181,13 +178,14 @@ class SingleDialer extends Emitter {
async _executeApp(url) {
this.logger.debug(`SingleDialer:_executeApp: executing ${url} after connect`);
try {
let auth;
let auth, method;
const app = Object.assign({}, this.application);
if (url.startsWith('/')) {
const savedUrl = url;
const or = app.originalRequest;
url = `${or.baseUrl}${url}`;
auth = or.auth;
method = this.method || or.method || 'POST';
this.logger.debug({originalUrl: savedUrl, normalizedUrl: url}, 'SingleDialer:_executeApp normalized url');
}
else {
@@ -196,9 +194,10 @@ class SingleDialer extends Emitter {
app.originalRequest = {
baseUrl: `${u.protocol}://${u.resource}${myPort}`
};
method = this.method || 'POST';
}
const tasks = await this.actionHook(url, this.method, auth);
const tasks = await this.actionHook({url, method, auth});
const allowedTasks = tasks.filter((task) => {
return [
TaskPreconditions.StableCall,
@@ -210,7 +209,14 @@ class SingleDialer extends Emitter {
}
this.logger.debug(`SingleDialer:_executeApp: executing ${tasks.length} tasks`);
const cs = new ConfirmCallSession({logger: this.logger, application: app, dlg: this.dlg, ep: this.ep, tasks});
const cs = new ConfirmCallSession({
logger: this.logger,
application: app,
dlg: this.dlg,
ep: this.ep,
callInfo: this.callInfo,
tasks
});
await cs.exec();
this.emit(this.dlg.connected ? 'accept' : 'decline');
} catch (err) {
@@ -220,9 +226,12 @@ class SingleDialer extends Emitter {
}
}
_notifyCallStatusChange({callStatus, sipStatus}) {
this.logger.debug(`SingleDialer:_notifyCallStatusChange: ${callStatus} ${sipStatus}`);
_notifyCallStatusChange({callStatus, sipStatus, duration}) {
assert((typeof duration === 'number' && callStatus === CallStatus.Completed) ||
(!duration && callStatus !== CallStatus.Completed),
'duration MUST be supplied when call completed AND ONLY when call completed');
this.callInfo.updateCallStatus(callStatus, sipStatus);
if (typeof duration === 'number') this.callInfo.duration = duration;
try {
this.notifyHook(this.application.call_status_hook);
} catch (err) {