mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-01-25 02:08:24 +00:00
Compare commits
4 Commits
v0.8.5-23
...
v0.5-branc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08809c14e9 | ||
|
|
bf557ba252 | ||
|
|
af79d92e71 | ||
|
|
1f4b175fc8 |
4
app.js
4
app.js
@@ -24,6 +24,8 @@ const {
|
||||
retrieveCall,
|
||||
deleteCall,
|
||||
listCalls,
|
||||
pushBack,
|
||||
listQueues,
|
||||
purgeCalls,
|
||||
retrieveSet
|
||||
} = require('@jambonz/realtimedb-helpers')({
|
||||
@@ -53,6 +55,8 @@ Object.assign(app.locals, {
|
||||
retrieveCall,
|
||||
deleteCall,
|
||||
listCalls,
|
||||
pushBack,
|
||||
listQueues,
|
||||
purgeCalls,
|
||||
retrieveSet,
|
||||
lookupAppBySid,
|
||||
|
||||
@@ -22,10 +22,10 @@ DROP TABLE IF EXISTS sip_gateways;
|
||||
|
||||
DROP TABLE IF EXISTS voip_carriers;
|
||||
|
||||
DROP TABLE IF EXISTS accounts;
|
||||
|
||||
DROP TABLE IF EXISTS applications;
|
||||
|
||||
DROP TABLE IF EXISTS accounts;
|
||||
|
||||
DROP TABLE IF EXISTS service_providers;
|
||||
|
||||
DROP TABLE IF EXISTS webhooks;
|
||||
@@ -150,6 +150,21 @@ 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 accounts
|
||||
(
|
||||
account_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
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) COMMENT 'webhook to call when members enter or leave a queue created by this account.',
|
||||
device_calling_application_sid CHAR(36) COMMENT 'application to use for outbound calling from an account',
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
webhook_secret VARCHAR(36),
|
||||
disable_cdrs BOOLEAN NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (account_sid)
|
||||
) COMMENT='An enterprise that uses the platform for comm services';
|
||||
|
||||
CREATE TABLE applications
|
||||
(
|
||||
application_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
@@ -177,20 +192,6 @@ ms_teams_fqdn VARCHAR(255),
|
||||
PRIMARY KEY (service_provider_sid)
|
||||
) COMMENT='A partition of the platform used by one service provider';
|
||||
|
||||
CREATE TABLE accounts
|
||||
(
|
||||
account_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
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',
|
||||
device_calling_application_sid CHAR(36) COMMENT 'application to use for outbound calling from an account',
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
webhook_secret VARCHAR(36),
|
||||
disable_cdrs BOOLEAN NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (account_sid)
|
||||
) COMMENT='An enterprise that uses the platform for comm services';
|
||||
|
||||
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);
|
||||
|
||||
@@ -242,6 +243,17 @@ ALTER TABLE lcr_carrier_set_entry ADD FOREIGN KEY lcr_route_sid_idxfk (lcr_route
|
||||
|
||||
ALTER TABLE lcr_carrier_set_entry ADD FOREIGN KEY voip_carrier_sid_idxfk_2 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
|
||||
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 registration_hook_sid_idxfk (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);
|
||||
|
||||
CREATE UNIQUE INDEX applications_idx_name ON applications (account_sid,name);
|
||||
|
||||
CREATE INDEX application_sid_idx ON applications (application_sid);
|
||||
@@ -257,15 +269,6 @@ ALTER TABLE applications ADD FOREIGN KEY messaging_hook_sid_idxfk (messaging_hoo
|
||||
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);
|
||||
ALTER TABLE service_providers ADD FOREIGN KEY registration_hook_sid_idxfk (registration_hook_sid) REFERENCES webhooks (webhook_sid);
|
||||
|
||||
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 registration_hook_sid_idxfk_1 (registration_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);
|
||||
ALTER TABLE service_providers ADD FOREIGN KEY registration_hook_sid_idxfk_1 (registration_hook_sid) REFERENCES webhooks (webhook_sid);
|
||||
|
||||
SET FOREIGN_KEY_CHECKS=1;
|
||||
|
||||
@@ -260,7 +260,7 @@
|
||||
<y>376.00</y>
|
||||
</location>
|
||||
<size>
|
||||
<width>254.00</width>
|
||||
<width>268.00</width>
|
||||
<height>120.00</height>
|
||||
</size>
|
||||
<zorder>10</zorder>
|
||||
@@ -529,7 +529,7 @@
|
||||
</location>
|
||||
<size>
|
||||
<width>380.00</width>
|
||||
<height>200.00</height>
|
||||
<height>220.00</height>
|
||||
</size>
|
||||
<zorder>4</zorder>
|
||||
<SQLField>
|
||||
@@ -583,13 +583,29 @@
|
||||
<referencesTable>webhooks</referencesTable>
|
||||
<referencesField><![CDATA[webhook_sid]]></referencesField>
|
||||
<referencesTable><![CDATA[webhooks]]></referencesTable>
|
||||
<sourceCardinality>4</sourceCardinality>
|
||||
<destinationCardinality>2</destinationCardinality>
|
||||
<sourceCardinality>2</sourceCardinality>
|
||||
<destinationCardinality>1</destinationCardinality>
|
||||
<referencesFieldUID><![CDATA[E046BA30-BC18-483C-A5C8-766E7160F574]]></referencesFieldUID>
|
||||
<referencesTableUID><![CDATA[64D64CB9-0990-4C68-BE71-F9FD43C2BE19]]></referencesTableUID>
|
||||
<forcedUnique><![CDATA[0]]></forcedUnique>
|
||||
<objectComment><![CDATA[webhook to call when devices underr this account attempt to register]]></objectComment>
|
||||
<uid><![CDATA[A75FAB8E-C2A1-4A05-A09E-6FF454109B6F]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[queue_event_hook_sid]]></name>
|
||||
<type><![CDATA[CHAR(36)]]></type>
|
||||
<referencesField>webhook_sid</referencesField>
|
||||
<referencesTable>webhooks</referencesTable>
|
||||
<referencesField><![CDATA[webhook_sid]]></referencesField>
|
||||
<referencesTable><![CDATA[webhooks]]></referencesTable>
|
||||
<sourceCardinality>2</sourceCardinality>
|
||||
<destinationCardinality>1</destinationCardinality>
|
||||
<referencesFieldUID><![CDATA[E046BA30-BC18-483C-A5C8-766E7160F574]]></referencesFieldUID>
|
||||
<referencesTableUID><![CDATA[64D64CB9-0990-4C68-BE71-F9FD43C2BE19]]></referencesTableUID>
|
||||
<forcedUnique><![CDATA[0]]></forcedUnique>
|
||||
<objectComment><![CDATA[webhook to call when members enter or leave a queue created by this account.]]></objectComment>
|
||||
<uid><![CDATA[3F1CD447-0ABF-4B4C-80A8-BAB0F73B64C9]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[device_calling_application_sid]]></name>
|
||||
<type><![CDATA[CHAR(36)]]></type>
|
||||
@@ -1145,17 +1161,17 @@
|
||||
<overviewPanelHidden><![CDATA[0]]></overviewPanelHidden>
|
||||
<pageBoundariesVisible><![CDATA[0]]></pageBoundariesVisible>
|
||||
<PageGridVisible><![CDATA[0]]></PageGridVisible>
|
||||
<RightSidebarWidth><![CDATA[1213.000000]]></RightSidebarWidth>
|
||||
<RightSidebarWidth><![CDATA[1415.000000]]></RightSidebarWidth>
|
||||
<sidebarIndex><![CDATA[2]]></sidebarIndex>
|
||||
<snapToGrid><![CDATA[0]]></snapToGrid>
|
||||
<SourceSidebarWidth><![CDATA[0.000000]]></SourceSidebarWidth>
|
||||
<SQLEditorFileFormatVersion><![CDATA[4]]></SQLEditorFileFormatVersion>
|
||||
<uid><![CDATA[58C99A00-06C9-478C-A667-C63842E088F3]]></uid>
|
||||
<windowHeight><![CDATA[977.000000]]></windowHeight>
|
||||
<windowLocationX><![CDATA[2718.000000]]></windowLocationX>
|
||||
<windowLocationY><![CDATA[1891.000000]]></windowLocationY>
|
||||
<windowHeight><![CDATA[1042.000000]]></windowHeight>
|
||||
<windowLocationX><![CDATA[3550.000000]]></windowLocationX>
|
||||
<windowLocationY><![CDATA[1884.000000]]></windowLocationY>
|
||||
<windowScrollOrigin><![CDATA[{369.5, 4}]]></windowScrollOrigin>
|
||||
<windowWidth><![CDATA[1490.000000]]></windowWidth>
|
||||
<windowWidth><![CDATA[1692.000000]]></windowWidth>
|
||||
</SQLDocumentInfo>
|
||||
<AllowsIndexRenamingOnInsert><![CDATA[1]]></AllowsIndexRenamingOnInsert>
|
||||
<defaultLabelExpanded><![CDATA[1]]></defaultLabelExpanded>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const getMysqlConnection = require('./mysql');
|
||||
const promisePool = require('./pool');
|
||||
|
||||
module.exports = {
|
||||
getMysqlConnection
|
||||
getMysqlConnection,
|
||||
promisePool
|
||||
};
|
||||
|
||||
10
lib/db/pool.js
Normal file
10
lib/db/pool.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const mysql = require('mysql2');
|
||||
const pool = mysql.createPool({
|
||||
host: process.env.JAMBONES_MYSQL_HOST,
|
||||
port: process.env.JAMBONES_MYSQL_PORT || 3306,
|
||||
user: process.env.JAMBONES_MYSQL_USER,
|
||||
password: process.env.JAMBONES_MYSQL_PASSWORD,
|
||||
database: process.env.JAMBONES_MYSQL_DATABASE,
|
||||
connectionLimit: process.env.JAMBONES_MYSQL_CONNECTION_LIMIT || 10
|
||||
});
|
||||
module.exports = pool.promise();
|
||||
@@ -3,17 +3,28 @@ const {getMysqlConnection} = require('../db');
|
||||
|
||||
const retrieveSql = `SELECT * from accounts acc
|
||||
LEFT JOIN webhooks AS rh
|
||||
ON acc.registration_hook_sid = rh.webhook_sid`;
|
||||
ON acc.registration_hook_sid = rh.webhook_sid
|
||||
LEFT JOIN webhooks AS qh
|
||||
ON acc.queue_event_hook_sid = qh.webhook_sid`;
|
||||
|
||||
|
||||
function transmogrifyResults(results) {
|
||||
return results.map((row) => {
|
||||
const obj = row.acc;
|
||||
let obj = row.acc;
|
||||
if (row.rh && Object.keys(row.rh).length && row.rh.url !== null) {
|
||||
Object.assign(obj, {registration_hook: row.rh});
|
||||
obj = {...obj, registration_hook: row.rh};
|
||||
delete obj.registration_hook.webhook_sid;
|
||||
}
|
||||
else obj.registration_hook = null;
|
||||
|
||||
if (row.qh && Object.keys(row.qh).length && row.qh.url !== null) {
|
||||
obj = {...obj, queue_event_hook: row.qh};
|
||||
delete obj.queue_event_hook.webhook_sid;
|
||||
}
|
||||
else obj.queue_event_hook = null;
|
||||
|
||||
delete obj.registration_hook_sid;
|
||||
delete obj.queue_event_hook_sid;
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
@@ -100,6 +111,10 @@ Account.fields = [
|
||||
name: 'registration_hook_sid',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'queue_event_hook_sid',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'device_calling_application_sid',
|
||||
type: 'string',
|
||||
|
||||
@@ -6,16 +6,14 @@ const Webhook = require('../../models/webhook');
|
||||
const ApiKey = require('../../models/api-key');
|
||||
const ServiceProvider = require('../../models/service-provider');
|
||||
const uuidv4 = require('uuid/v4');
|
||||
const decorate = require('./decorate');
|
||||
const snakeCase = require('../../utils/snake-case');
|
||||
const sysError = require('./error');
|
||||
const preconditions = {
|
||||
'add': validateAdd,
|
||||
'update': validateUpdate,
|
||||
'delete': validateDelete
|
||||
};
|
||||
const {promisePool} = require('../../db');
|
||||
const {hasAccountPermissions} = require('./utils');
|
||||
let idx = 0;
|
||||
|
||||
router.use('/:sid/Queues', hasAccountPermissions, require('./queues'));
|
||||
|
||||
function coerceNumbers(callInfo) {
|
||||
if (Array.isArray(callInfo)) {
|
||||
return callInfo.map((ci) => {
|
||||
@@ -207,6 +205,9 @@ async function validateAdd(req) {
|
||||
if (req.body.registration_hook && typeof req.body.registration_hook !== 'object') {
|
||||
throw new DbErrorBadRequest('\'registration_hook\' must be an object when adding an account');
|
||||
}
|
||||
if (req.body.queue_event_hook && typeof req.body.queue_event_hook !== 'object') {
|
||||
throw new DbErrorBadRequest('\'queue_event_hook\' must be an object when adding an account');
|
||||
}
|
||||
}
|
||||
async function validateUpdate(req, sid) {
|
||||
if (req.user.hasAccountAuth && req.user.account_sid !== sid) {
|
||||
@@ -235,7 +236,32 @@ async function validateDelete(req, sid) {
|
||||
}
|
||||
}
|
||||
|
||||
decorate(router, Account, ['delete'], preconditions);
|
||||
/* delete */
|
||||
router.delete('/:sid', async(req, res) => {
|
||||
const sid = req.params.sid;
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
await validateDelete(req, sid);
|
||||
|
||||
/* delete associated webhooks */
|
||||
const webhooks = [];
|
||||
const [results] = await promisePool.query('SELECT * FROM accounts where account_sid = ?', sid);
|
||||
if (results.length) {
|
||||
if (results[0].registration_hook_sid) webhooks.push(results[0].registration_hook_sid);
|
||||
if (results[0].queue_event_hook_sid) webhooks.push(results[0].queue_event_hook_sid);
|
||||
}
|
||||
await Account.remove(sid);
|
||||
|
||||
for (const wh of webhooks) {
|
||||
promisePool.execute('DELETE from webhooks where webhook_sid = ?', [wh])
|
||||
.catch((err) => logger.info({err, webhooks}, 'DELETE /Accounts Error deleting webhooks'));
|
||||
}
|
||||
|
||||
res.sendStatus(204);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
/* add */
|
||||
router.post('/', async(req, res) => {
|
||||
@@ -245,7 +271,7 @@ router.post('/', async(req, res) => {
|
||||
|
||||
// create webhooks if provided
|
||||
const obj = Object.assign({}, req.body);
|
||||
for (const prop of ['registration_hook']) {
|
||||
for (const prop of ['registration_hook', 'queue_event_hook']) {
|
||||
if (obj[prop]) {
|
||||
obj[`${prop}_sid`] = await Webhook.make(obj[prop]);
|
||||
delete obj[prop];
|
||||
@@ -295,43 +321,48 @@ router.put('/:sid', async(req, res) => {
|
||||
|
||||
// create webhooks if provided
|
||||
const obj = Object.assign({}, req.body);
|
||||
if (null !== obj.registration_hook) {
|
||||
for (const prop of ['registration_hook']) {
|
||||
if (prop in obj && Object.keys(obj[prop]).length) {
|
||||
if ('webhook_sid' in obj[prop]) {
|
||||
const sid = obj[prop]['webhook_sid'];
|
||||
delete obj[prop]['webhook_sid'];
|
||||
await Webhook.update(sid, obj[prop]);
|
||||
}
|
||||
else {
|
||||
const sid = await Webhook.make(obj[prop]);
|
||||
obj[`${prop}_sid`] = sid;
|
||||
}
|
||||
for (const prop of ['registration_hook', 'queue_event_hook']) {
|
||||
if (prop in obj && Object.keys(obj[prop]).length) {
|
||||
if ('webhook_sid' in obj[prop]) {
|
||||
const sid = obj[prop]['webhook_sid'];
|
||||
delete obj[prop]['webhook_sid'];
|
||||
await Webhook.update(sid, obj[prop]);
|
||||
}
|
||||
else {
|
||||
obj[`${prop}_sid`] = null;
|
||||
const sid = await Webhook.make(obj[prop]);
|
||||
obj[`${prop}_sid`] = sid;
|
||||
}
|
||||
delete obj[prop];
|
||||
}
|
||||
delete obj[prop];
|
||||
}
|
||||
|
||||
await validateUpdate(req, sid);
|
||||
|
||||
if (Object.keys(obj).length) {
|
||||
let orphanedHook;
|
||||
if (null === obj.registration_hook) {
|
||||
const orphanedHooks = [];
|
||||
if (null === obj.registration_hook || null == obj.queue_event_hook) {
|
||||
const results = await Account.retrieve(sid);
|
||||
if (results.length && results[0].registration_hook_sid) orphanedHook = results[0].registration_hook_sid;
|
||||
obj.registration_hook_sid = null;
|
||||
delete obj.registration_hook;
|
||||
if (results.length) {
|
||||
if (results[0].registration_hook_sid) orphanedHooks.push(results[0].registration_hook_sid);
|
||||
if (results[0].queue_event_hook_sid) orphanedHooks.push(results[0].queue_event_hook_sid);
|
||||
}
|
||||
if (null === obj.registration_hook) {
|
||||
obj.registration_hook_sid = null;
|
||||
delete obj.registration_hook;
|
||||
}
|
||||
if (null === obj.queue_event_hook) {
|
||||
obj.queue_event_hook_sid = null;
|
||||
delete obj.queue_event_hook;
|
||||
}
|
||||
}
|
||||
logger.info({obj}, `about to update Account ${sid}`);
|
||||
const rowsAffected = await Account.update(sid, obj);
|
||||
if (rowsAffected === 0) {
|
||||
return res.status(404).end();
|
||||
}
|
||||
if (orphanedHook) {
|
||||
await Webhook.remove(orphanedHook);
|
||||
if (orphanedHooks.length) {
|
||||
for (const sid in orphanedHooks) {
|
||||
await Webhook.remove(sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
20
lib/routes/api/queues.js
Normal file
20
lib/routes/api/queues.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const router = require('express').Router();
|
||||
const sysError = require('./error');
|
||||
const {parseAccountSid} = require('./utils');
|
||||
|
||||
/**
|
||||
* retrieve queues for an account
|
||||
*/
|
||||
router.get('/', async(req, res) => {
|
||||
const account_sid = parseAccountSid(req);
|
||||
const {logger, listQueues} = req.app.locals;
|
||||
try {
|
||||
const queues = await listQueues(account_sid, req.query.name);
|
||||
res.status(200).json(queues);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
module.exports = router;
|
||||
40
lib/routes/api/utils.js
Normal file
40
lib/routes/api/utils.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const parseServiceProviderSid = (req) => {
|
||||
const arr = /ServiceProviders\/([^\/]*)/.exec(req.originalUrl);
|
||||
if (arr) return arr[1];
|
||||
};
|
||||
|
||||
const parseAccountSid = (req) => {
|
||||
const arr = /Accounts\/([^\/]*)/.exec(req.originalUrl);
|
||||
if (arr) return arr[1];
|
||||
};
|
||||
|
||||
const hasAccountPermissions = (req, res, next) => {
|
||||
if (req.user.hasScope('admin')) return next();
|
||||
if (req.user.hasScope('account')) {
|
||||
const account_sid = parseAccountSid(req);
|
||||
if (account_sid === req.user.account_sid) return next();
|
||||
}
|
||||
res.status(403).json({
|
||||
status: 'fail',
|
||||
message: 'insufficient privileges'
|
||||
});
|
||||
};
|
||||
|
||||
const hasServiceProviderPermissions = (req, res, next) => {
|
||||
if (req.user.hasScope('admin')) return next();
|
||||
if (req.user.hasScope('service_provider')) {
|
||||
const service_provider_sid = parseServiceProviderSid(req);
|
||||
if (service_provider_sid === req.user.service_provider_sid) return next();
|
||||
}
|
||||
res.status(403).json({
|
||||
status: 'fail',
|
||||
message: 'insufficient privileges'
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
parseAccountSid,
|
||||
parseServiceProviderSid,
|
||||
hasAccountPermissions,
|
||||
hasServiceProviderPermissions
|
||||
};
|
||||
@@ -981,6 +981,9 @@ paths:
|
||||
registration_hook:
|
||||
$ref: '#/components/schemas/Webhook'
|
||||
description: authentication webhook for registration
|
||||
queue_event_hook:
|
||||
$ref: '#/components/schemas/Webhook'
|
||||
description: webhook for queue events
|
||||
service_provider_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
@@ -1399,7 +1402,36 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
|
||||
|
||||
/Accounts/{AccountSid}/Queues:
|
||||
get:
|
||||
summary: list queues for an Account
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: name
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: a queue name or glob-style pattern matching one or more queues to return
|
||||
responses:
|
||||
200:
|
||||
description: list of queues for this account matching the name query param, if provided
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Queue'
|
||||
500:
|
||||
description: system error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
|
||||
/Accounts/{AccountSid}/Calls:
|
||||
post:
|
||||
summary: create a call
|
||||
@@ -1744,6 +1776,9 @@ components:
|
||||
registration_hook:
|
||||
$ref: '#/components/schemas/Webhook'
|
||||
description: authentication webhook for registration
|
||||
queue_event_hook:
|
||||
$ref: '#/components/schemas/Webhook'
|
||||
description: webhook for queue events
|
||||
device_calling_application_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
@@ -1983,6 +2018,18 @@ components:
|
||||
- from
|
||||
- to
|
||||
example: {"from": "13394445678", "to": "16173333456", "text": "please call when you can"}
|
||||
|
||||
Queue:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: support
|
||||
length:
|
||||
type: number
|
||||
format: integer
|
||||
example: 5
|
||||
required:
|
||||
- name
|
||||
- length
|
||||
example: {"name": "new-orders", length: 12}
|
||||
security:
|
||||
- bearerAuth: []
|
||||
12
package.json
12
package.json
@@ -5,7 +5,7 @@
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
"test": "NODE_ENV=test JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_MYSQL_PORT=3360 JAMBONES_REDIS_HOST=localhost JAMBONES_LOGLEVEL=error JAMBONES_CREATE_CALL_URL=http://localhost/v1/createCall node test/ | ./node_modules/.bin/tap-spec",
|
||||
"test": "NODE_ENV=test JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_MYSQL_PORT=3360 JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error JAMBONES_CREATE_CALL_URL=http://localhost/v1/createCall node test/ ",
|
||||
"integration-test": "NODE_ENV=test JAMBONES_TIME_SERIES_HOST=127.0.0.1 AWS_REGION='us-east-1' JAMBONES_CURRENCY=USD JWT_SECRET=foobarbazzle 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=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=debug JAMBONES_CREATE_CALL_URL=http://localhost/v1/createCall node test/serve-integration.js",
|
||||
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
|
||||
"jslint": "eslint app.js lib"
|
||||
@@ -16,11 +16,8 @@
|
||||
"url": "https://github.com/jambonz/jambonz-api-server.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jambonz/db-helpers": "^0.5.5",
|
||||
"@jambonz/messaging-382com": "0.0.2",
|
||||
"@jambonz/messaging-peerless": "0.0.9",
|
||||
"@jambonz/messaging-simwood": "0.0.4",
|
||||
"@jambonz/realtimedb-helpers": "0.2.19",
|
||||
"@jambonz/db-helpers": "^0.6.11",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.3",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"mysql2": "^2.2.5",
|
||||
@@ -34,11 +31,10 @@
|
||||
"yamljs": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"blue-tape": "^1.0.0",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"nyc": "^15.1.0",
|
||||
"request-promise-native": "^1.0.9",
|
||||
"tap-spec": "^5.0.0"
|
||||
"tape": "^5.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape') ;
|
||||
const ADMIN_TOKEN = '38700987-c7a4-4685-a5bb-af378f9734de';
|
||||
const authAdmin = {bearer: ADMIN_TOKEN};
|
||||
const request = require('request-promise-native').defaults({
|
||||
@@ -16,6 +16,7 @@ process.on('unhandledRejection', (reason, p) => {
|
||||
|
||||
test('account tests', async(t) => {
|
||||
const app = require('../app');
|
||||
const {pushBack} = app.locals;
|
||||
let sid;
|
||||
try {
|
||||
let result;
|
||||
@@ -36,6 +37,10 @@ test('account tests', async(t) => {
|
||||
registration_hook: {
|
||||
url: 'http://example.com/reg',
|
||||
method: 'get'
|
||||
},
|
||||
queue_event_hook: {
|
||||
url: 'http://example.com/q',
|
||||
method: 'post'
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -68,9 +73,10 @@ test('account tests', async(t) => {
|
||||
json: true,
|
||||
});
|
||||
let regHook = result[0].registration_hook;
|
||||
let qHook = result[0].queue_event_hook;
|
||||
t.ok(result.length === 1 &&
|
||||
Object.keys(regHook).length == 4, 'successfully queried all accounts');
|
||||
|
||||
Object.keys(regHook).length == 4 && Object.keys(qHook).length == 4, 'successfully queried all accounts');
|
||||
|
||||
/* query one accounts */
|
||||
result = await request.get(`/Accounts/${sid}`, {
|
||||
auth: authAdmin,
|
||||
@@ -104,7 +110,6 @@ test('account tests', async(t) => {
|
||||
auth: authAdmin,
|
||||
json: true,
|
||||
});
|
||||
//console.log(`retrieved account after update: ${JSON.stringify(result)}`);
|
||||
t.ok(Object.keys(result.registration_hook).length === 4, 'successfully removed a hook from account');
|
||||
|
||||
/* assign phone number to account */
|
||||
@@ -118,6 +123,36 @@ test('account tests', async(t) => {
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully assigned phone number to account');
|
||||
|
||||
/* retrieve queues for account */
|
||||
result = await request.get(`/Accounts/${sid}/Queues`, {
|
||||
auth: {bearer: accountLevelToken},
|
||||
json: true,
|
||||
resolveWithFullResponse: true,
|
||||
});
|
||||
t.ok(result.statusCode === 200 && 0 === result.body.length,
|
||||
'successfully retrieved an empty array when no queues exist');
|
||||
|
||||
await pushBack(`queue:${sid}:customer-support`, 'https://ip:300/v1/enqueue/foobar');
|
||||
await pushBack(`queue:${sid}:customer-support`, 'https://ip:300/v1/enqueue/bazzle');
|
||||
await pushBack(`queue:${sid}:sales-new-orders`, 'https://ip:300/v1/enqueue/bazzle');
|
||||
await pushBack(`queue:${sid}:sales-returns`, 'https://ip:300/v1/enqueue/bazzle');
|
||||
|
||||
result = await request.get(`/Accounts/${sid}/Queues`, {
|
||||
auth: {bearer: accountLevelToken},
|
||||
json: true,
|
||||
resolveWithFullResponse: true,
|
||||
});
|
||||
console.log(`retrieved queues: ${result.statusCode}: ${JSON.stringify(result.body)}`);
|
||||
//t.ok(result.statusCode === 200 && 0 === result.body.length,
|
||||
// 'successfully retrieved an empty array when no queues exist');
|
||||
|
||||
result = await request.get(`/Accounts/${sid}/Queues?name=sales-*`, {
|
||||
auth: {bearer: accountLevelToken},
|
||||
json: true,
|
||||
resolveWithFullResponse: true,
|
||||
});
|
||||
console.log(`retrieved queues: ${result.statusCode}: ${JSON.stringify(result.body)}`);
|
||||
|
||||
/* cannot delete account that has phone numbers assigned */
|
||||
result = await request.delete(`/Accounts/${sid}`, {
|
||||
auth: authAdmin,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape') ;
|
||||
const ADMIN_TOKEN = '38700987-c7a4-4685-a5bb-af378f9734de';
|
||||
const authAdmin = {bearer: ADMIN_TOKEN};
|
||||
const request = require('request-promise-native').defaults({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape').test ;
|
||||
const ADMIN_TOKEN = '38700987-c7a4-4685-a5bb-af378f9734de';
|
||||
const authAdmin = {bearer: ADMIN_TOKEN};
|
||||
const request = require('request-promise-native').defaults({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape') ;
|
||||
const exec = require('child_process').exec ;
|
||||
|
||||
test('creating jambones_test database', (t) => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const test = require('blue-tape');
|
||||
//const test = require('tape').test ;
|
||||
const test = require('tape');
|
||||
const exec = require('child_process').exec ;
|
||||
|
||||
test('starting docker network..', (t) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape');
|
||||
const test = require('tape');
|
||||
const exec = require('child_process').exec ;
|
||||
|
||||
test('stopping docker network..', (t) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape') ;
|
||||
const ADMIN_TOKEN = '38700987-c7a4-4685-a5bb-af378f9734de';
|
||||
const authAdmin = {bearer: ADMIN_TOKEN};
|
||||
const request = require('request-promise-native').defaults({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape') ;
|
||||
const ADMIN_TOKEN = '38700987-c7a4-4685-a5bb-af378f9734de';
|
||||
const authAdmin = {bearer: ADMIN_TOKEN};
|
||||
const request = require('request-promise-native').defaults({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('tape').test ;
|
||||
const test = require('tape') ;
|
||||
const exec = require('child_process').exec ;
|
||||
const pwd = process.env.CI ? '' : '-p$MYSQL_ROOT_PASSWORD';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape') ;
|
||||
const ADMIN_TOKEN = '38700987-c7a4-4685-a5bb-af378f9734de';
|
||||
const authAdmin = {bearer: ADMIN_TOKEN};
|
||||
const request = require('request-promise-native').defaults({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape') ;
|
||||
const ADMIN_TOKEN = '38700987-c7a4-4685-a5bb-af378f9734de';
|
||||
const authAdmin = {bearer: ADMIN_TOKEN};
|
||||
const request = require('request-promise-native').defaults({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape') ;
|
||||
const ADMIN_TOKEN = '38700987-c7a4-4685-a5bb-af378f9734de';
|
||||
const authAdmin = {bearer: ADMIN_TOKEN};
|
||||
const request = require('request-promise-native').defaults({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const test = require('blue-tape').test ;
|
||||
const test = require('tape') ;
|
||||
const ADMIN_TOKEN = '38700987-c7a4-4685-a5bb-af378f9734de';
|
||||
const authAdmin = {bearer: ADMIN_TOKEN};
|
||||
const request = require('request-promise-native').defaults({
|
||||
|
||||
Reference in New Issue
Block a user