mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-07-24 21:12:10 +00:00
added initial support for creating calls through REST API
This commit is contained in:
@@ -24,6 +24,7 @@ app.use((err, req, res, next) => {
|
||||
logger.error(err, 'burped error');
|
||||
res.status(err.status || 500).json({msg: err.message});
|
||||
});
|
||||
logger.info(`listening for HTTP traffic on port ${PORT}`);
|
||||
app.listen(PORT);
|
||||
|
||||
module.exports = app;
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
"password": "secret",
|
||||
"database": "jambones",
|
||||
"connectionLimit": 10
|
||||
},
|
||||
"services": {
|
||||
"createCall": "http://feature.server/createCall:3000"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
const router = require('express').Router();
|
||||
const request = require('request');
|
||||
const config = require('config');
|
||||
const {DbErrorBadRequest, DbErrorUnprocessableRequest} = require('../../utils/errors');
|
||||
const Account = require('../../models/account');
|
||||
const Webhook = require('../../models/webhook');
|
||||
@@ -11,6 +13,54 @@ const preconditions = {
|
||||
'delete': validateDelete
|
||||
};
|
||||
|
||||
function validateTo(to) {
|
||||
if (to && typeof to === 'object') {
|
||||
switch (to.type) {
|
||||
case 'phone':
|
||||
if (typeof to.number === 'string') return;
|
||||
break;
|
||||
case 'user':
|
||||
if (typeof to.name === 'string') return;
|
||||
break;
|
||||
case 'sip':
|
||||
if (typeof to.sipUri === 'string') return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new DbErrorBadRequest(`missing or invalid to property: ${to}`);
|
||||
}
|
||||
async function validateCreateCall(logger, sid, req) {
|
||||
const {lookupApplicationBySid} = require('jambonz-db-helpers')(config.get('mysql'), logger);
|
||||
const obj = req.body;
|
||||
|
||||
if (req.user.account_sid !== sid) throw new DbErrorBadRequest(`unauthorized createCall request for account ${sid}`);
|
||||
|
||||
if (!obj.from) throw new DbErrorBadRequest('missing from parameter');
|
||||
validateTo(obj.to);
|
||||
|
||||
if (obj.application_sid) {
|
||||
try {
|
||||
logger.debug(`Accounts:validateCreateCall retrieving application ${obj.application_sid}`);
|
||||
const application = await lookupApplicationBySid(obj.application_sid);
|
||||
logger.debug(`Accounts:validateCreateCall retrieved application ${JSON.stringify(application)}`);
|
||||
Object.assign(obj, {
|
||||
call_hook: application.call_hook,
|
||||
call_status_hook: application.call_status_hook,
|
||||
speech_synthesis_vendor: application.speech_synthesis_vendor,
|
||||
speech_synthesis_voice: application.speech_synthesis_voice,
|
||||
speech_recognizer_vendor: application.speech_recognizer_vendor,
|
||||
speech_recognizer_language: application.speech_recognizer_language
|
||||
});
|
||||
logger.debug({obj, application}, 'Accounts:validateCreateCall augmented with application settings');
|
||||
} catch (err) {
|
||||
logger.error(err, `Accounts:validateCreateCall error retrieving application for sid ${obj.application_sid}`);
|
||||
throw new DbErrorBadRequest(`application_sid not found ${obj.application_sid}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj.call_hook || (obj.call_hook && !obj.call_hook.url)) throw new DbErrorBadRequest('either url or application_sid required');
|
||||
}
|
||||
|
||||
async function validateAdd(req) {
|
||||
/* account-level token can not be used to add accounts */
|
||||
if (req.user.hasAccountAuth) {
|
||||
@@ -152,4 +202,35 @@ router.put('/:sid', async(req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:sid/Calls', async(req, res) => {
|
||||
const sid = req.params.sid;
|
||||
const logger = req.app.locals.logger;
|
||||
|
||||
try {
|
||||
const serviceUrl = config.get('services.createCall');
|
||||
await validateCreateCall(logger, sid, req);
|
||||
|
||||
logger.debug({payload: req.body}, `sending POST to ${serviceUrl}`);
|
||||
request({
|
||||
url: serviceUrl,
|
||||
method: 'POST',
|
||||
json: true,
|
||||
body: req.body
|
||||
}, (err, response, body) => {
|
||||
if (err) {
|
||||
logger.error(err, `Error sending createCall POST to ${serviceUrl}`);
|
||||
return res.send(500);
|
||||
}
|
||||
if (response.statusCode !== 201) {
|
||||
logger.error({statusCode: response.statusCode}, `Non-success response returned by createCall ${serviceUrl}`);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
res.status(201).json(body);
|
||||
});
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
+96
-65
@@ -1,7 +1,7 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Jambones REST API
|
||||
description: Jambones REST API
|
||||
title: jambonz REST API
|
||||
description: jambonz REST API
|
||||
contact:
|
||||
email: daveh@drachtio.org
|
||||
license:
|
||||
@@ -1051,7 +1051,7 @@ paths:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
|
||||
|
||||
/Accounts/{AccountSid}/Applications/{ApplicationSid}/Calls:
|
||||
/Accounts/{AccountSid}/Calls:
|
||||
post:
|
||||
summary: create a call
|
||||
operationId: createCall
|
||||
@@ -1061,11 +1061,6 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: ApplicationSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
@@ -1075,39 +1070,28 @@ paths:
|
||||
- to
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
application_sid:
|
||||
type: string
|
||||
description: |
|
||||
The url of the web application to control this call.
|
||||
If not provided, the url specified in the application will be used
|
||||
example: https://mycompany.com/deliver-message.json
|
||||
format: uuid
|
||||
description: The application to use to control this call. Either applicationSid or url is required.
|
||||
call_hook:
|
||||
$ref: '#/components/Webhook'
|
||||
call_status_hook:
|
||||
$ref: '#/components/Webhook'
|
||||
from:
|
||||
type: string
|
||||
description: The calling party number
|
||||
example: 16172375089
|
||||
example: +16172375089
|
||||
timeout:
|
||||
type: integer
|
||||
description: the number of seconds to wait for call to be answered. Defaults to 60.
|
||||
tag:
|
||||
type: object
|
||||
description: initial set of customer-supplied metadata to associate with the call (see jambonz 'tag' verb)
|
||||
to:
|
||||
type: string
|
||||
description: The telephone number or sip endpoint to call
|
||||
example: 16172228000
|
||||
recordingUrl:
|
||||
type: string
|
||||
format: url
|
||||
description: A websocket url to stream the call audio to
|
||||
example: wss://myserver.com
|
||||
recordingMix:
|
||||
type: string
|
||||
description: whether to record either or both parties
|
||||
enum:
|
||||
- caller
|
||||
- callee
|
||||
- stereo
|
||||
- mixed
|
||||
example: stereo
|
||||
statusCallback:
|
||||
type: string
|
||||
format: url
|
||||
description: The url to send call status change events to
|
||||
example: https://company.com/status
|
||||
$ref: '#/components/Target'
|
||||
description: destination for call
|
||||
|
||||
responses:
|
||||
201:
|
||||
description: call successfully created
|
||||
@@ -1317,35 +1301,6 @@ components:
|
||||
- username
|
||||
- domain
|
||||
- sip_contact
|
||||
Call:
|
||||
type: object
|
||||
properties:
|
||||
call_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
application:
|
||||
$ref: '#/components/schemas/Application'
|
||||
parent_call:
|
||||
$ref: '#/components/schemas/Call'
|
||||
direction:
|
||||
type: string
|
||||
enum:
|
||||
- inbound
|
||||
- outbound
|
||||
phone_number:
|
||||
$ref: '#/components/schemas/PhoneNumber'
|
||||
inbound_user:
|
||||
$ref: '#/components/schemas/RegisteredUser'
|
||||
outbound_user:
|
||||
$ref: '#/components/schemas/RegisteredUser'
|
||||
calling_number:
|
||||
type: string
|
||||
called_number:
|
||||
type: string
|
||||
required:
|
||||
- call_sid
|
||||
- application
|
||||
- direction
|
||||
Webhook:
|
||||
type: object
|
||||
properties:
|
||||
@@ -1363,6 +1318,82 @@ components:
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
Call:
|
||||
type: object
|
||||
properties:
|
||||
account_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
application_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
api_version:
|
||||
type: string
|
||||
caller_name:
|
||||
type: string
|
||||
call_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
date_created:
|
||||
type: string
|
||||
date_updated:
|
||||
type: string
|
||||
direction:
|
||||
type: string
|
||||
enum:
|
||||
- inbound
|
||||
- outbound
|
||||
duration:
|
||||
type: integer
|
||||
end_time:
|
||||
type: string
|
||||
forwarded_from:
|
||||
type: string
|
||||
from:
|
||||
type: string
|
||||
parent_call_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
phone_number_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
start_time:
|
||||
type: string
|
||||
to:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
required:
|
||||
- api_version
|
||||
- account-sid
|
||||
- call_sid
|
||||
- date_created
|
||||
- from
|
||||
- to
|
||||
- uri
|
||||
Target:
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- phone
|
||||
- sip
|
||||
- user
|
||||
number:
|
||||
type: string
|
||||
sipUri:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
auth:
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
|
||||
security:
|
||||
- bearerAuth: []
|
||||
@@ -17,6 +17,7 @@
|
||||
"config": "^3.2.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"jambonz-db-helpers": "^0.2.0",
|
||||
"mysql2": "^2.0.2",
|
||||
"passport": "^0.4.0",
|
||||
"passport-http-bearer": "^1.0.1",
|
||||
|
||||
Reference in New Issue
Block a user