add support for calls.create to create a new call using REST

This commit is contained in:
Dave Horton
2021-02-19 11:56:33 -05:00
parent a1e7ed31c4
commit 0696fe0885
6 changed files with 96 additions and 27 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
const {validate, specs} = require('./utils');
const {validate, specs} = require('../utils');
class WebhookResponse {
constructor() {
@@ -20,7 +20,7 @@ class WebhookResponse {
addVerb(verb, payload) {
validate(verb, payload);
this.payload.push({verb, ...payload});
this.payload.push({verb: verb.replace('_', ':'), ...payload});
return this;
}
}
+26 -16
View File
@@ -1,4 +1,4 @@
const assert = require('assert');
const {validateCallUpdate, validateCallCreate} = require('../utils');
class Calls {
constructor(accountSid, apiKey, opts) {
@@ -8,22 +8,32 @@ class Calls {
['post', 'put', 'get', 'del'].forEach((m) => this[m] = opts[m]);
}
/**
* Create a new outbound call
* @param {*} opts - see https://docs.jambonz.org/rest/#create-a-call for details
*/
async create(opts) {
validateCallCreate(opts);
const res = await this.post(`Accounts/${this.accountSid}/Calls`, opts);
return res.sid;
}
/**
* Update a call in progress. Available operations are:
* - mute / unmute
* - pause / resume a "listen" audio stream
* - hang up the call
* - play a whisper prompt to one party
* - redirect the call to a new application
* @param {*} callSid - identifies call leg to operate on
* @param {*} opts - see https://docs.jambonz.org/rest/#updating-a-call for details
*/
async update(callSid, opts) {
const {call_hook, call_status, listen_status, mute_status, whisper} = opts;
assert.ok(call_hook || call_status || listen_status || mute_status || whisper,
`calls.update: invalid request ${JSON.stringify(opts)}`);
if (call_status) assert.ok(['completed', 'no-answer'].includes(call_status),
`invalid call_status: ${call_status}, must be 'completed' or 'no-answer'`);
if (mute_status) assert.ok(['mute', 'unmute'].includes(mute_status),
`invalid mute_status: ${mute_status}, must be 'mute' or 'unmute'`);
if (whisper) assert.ok(whisper.verb,
`invalid whisper: ${JSON.stringify(whisper)}, must be a 'play' or 'say' verb`);
await this.post(`Accounts/${this.accountSid}/Calls/${callSid}`, opts);
validateCallUpdate(opts);
const res = await this.put(`Accounts/${this.accountSid}/Calls/${callSid}`, opts);
if (res.statusCode === 404) throw new Error(`Calls.update: call_sid ${callSid} not found`);
if (res.statusCode !== 204) throw new Error(`Calls.update: unexpected status code ${res.statusCode}`);
}
}
+4 -4
View File
@@ -17,10 +17,10 @@ class Jambonz {
const headers = {'Authorization': `Bearer ${apiKey}`};
const post = bent(baseUrl, 'POST', 'buffer', headers, 200, 201, 202);
const put = bent(baseUrl, 'POST', 'buffer', headers, 204);
const get = bent(baseUrl, 'GET', 'buffer', headers, 200);
const del = bent(baseUrl, 'DELETE', 'buffer', headers, 204);
const post = bent(baseUrl, 'POST', 'json', headers, 201);
const put = bent(baseUrl, 'PUT', headers, 204);
const get = bent(baseUrl, 'GET', 'json', headers, 200);
const del = bent(baseUrl, 'DELETE', headers, 204);
this.calls = new Calls(accountSid, apiKey, {baseUrl, post, put, get, del});
}
+40 -2
View File
@@ -1,6 +1,6 @@
const debug = require('debug')('jambonz:jambonz-node');
const assert = require('assert');
const specs = new Map(Object.entries(require('./specs.json')));
const specs = new Map(Object.entries(require('./jambonz/specs.json')));
/**
* copied from jambonz-feature-server/lib/tasks.js
@@ -70,7 +70,45 @@ function validate(name, data) {
* end of copy
*/
/**
* Validate the payload for an updateCall request
*/
const validateCallUpdate = (opts) => {
const {call_hook, call_status, listen_status, mute_status, whisper} = opts;
assert.ok(call_hook || call_status || listen_status || mute_status || whisper,
`calls.update: invalid request ${JSON.stringify(opts)}`);
if (call_status) assert.ok(['completed', 'no-answer'].includes(call_status),
`invalid call_status: ${call_status}, must be 'completed' or 'no-answer'`);
if (mute_status) assert.ok(['mute', 'unmute'].includes(mute_status),
`invalid mute_status: ${mute_status}, must be 'mute' or 'unmute'`);
if (whisper) assert.ok(whisper.verb,
`invalid whisper: ${JSON.stringify(whisper)}, must be a 'play' or 'say' verb`);
};
/**
* Validate the payload for a createCall request
*/
const validateCallCreate = (opts) => {
const {application_sid, call_hook, call_status_hook, from, to} = opts;
assert.ok(from && typeof from === 'string', 'calls.create: from property is required');
assert.ok(to && typeof to === 'object', 'calls.create: to property is required');
assert.ok(application_sid || call_hook, 'calls.create: application_sid or call_hook is required');
assert.ok(call_status_hook || !call_hook, 'calls.create: call_status_hook is required when call_hook is used');
const hookUrl = typeof call_hook === 'object' ? call_hook.url : call_hook;
if (hookUrl) assert.ok(/^https?:/.test(hookUrl), 'call_hook must be an absolute url');
const hookStatusUrl = typeof call_status_hook === 'object' ? call_status_hook.url : call_status_hook;
if (hookStatusUrl) assert.ok(/^https?:/.test(hookStatusUrl), 'call_status_hook must be an absolute url');
};
module.exports = {
validate,
specs
specs,
validateCallUpdate,
validateCallCreate
};