Compare commits

..

12 Commits

Author SHA1 Message Date
dependabot[bot]
10009d903e Bump undici from 5.11.0 to 5.19.1 (#115)
Bumps [undici](https://github.com/nodejs/undici) from 5.11.0 to 5.19.1.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.11.0...v5.19.1)

---
updated-dependencies:
- dependency-name: undici
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-16 17:33:12 -05:00
Snyk bot
69a72c5e43 fix: upgrade aws-sdk from 2.1238.0 to 2.1302.0 (#110)
Snyk has created this PR to upgrade aws-sdk from 2.1238.0 to 2.1302.0.

See this package in npm:
https://www.npmjs.com/package/aws-sdk

See this project in Snyk:
https://app.snyk.io/org/davehorton/project/b7e09765-19b2-4aa5-90ba-10432e250041?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-02-16 13:31:57 -05:00
Dave Horton
f7f3881d70 update to @jambonz/verb-specifications (#109) 2023-02-15 10:15:06 -05:00
Hoan Luu Huu
4d48c6946c feat: start using verb-specifications (#107)
* feat: start using verb-specifications

* fix: verb specification v2

* fix vulnerabilities

---------

Co-authored-by: Quan HL <quanluuhoang8@gmail.com>
Co-authored-by: Dave Horton <daveh@beachdognet.com>
2023-02-14 17:24:19 -05:00
Snyk bot
5b48fc8a07 fix: Dockerfile to reduce vulnerabilities (#106)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314623
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314624
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314624
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314641
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314643
2023-02-12 22:50:13 -05:00
Hoan Luu Huu
f46be95551 feat: nvidia speech credential (#105)
* feat: nvidia speech credential

* fix: riva_server_uri

* fix: riva_server_uri

---------

Co-authored-by: Quan HL <quanluuhoang8@gmail.com>
2023-02-10 08:31:49 -05:00
EgleH
d4f2be3dc1 Add check for users to delete function (#104)
* add check for users to delete function

* fix typo

* fix active admin check

---------

Co-authored-by: eglehelms <e.helms@cognigy.com>
2023-02-08 07:16:29 -05:00
Dave Horton
a46c24b3aa change applications.app_json to TEXT as it could hold large strings 2023-02-03 16:34:11 -05:00
AFelix53
f5c833720a feat: added twilio gateways (#99) 2023-01-30 10:16:27 -05:00
EgleH
4d2cc15de4 Bug/speech creds get all with no sp sid (#102)
* backwards compatibility

* fetch account and sp speech remove duplicates

* fix retrieval of SP credentials associated to an account level user

* update gh actions

---------

Co-authored-by: eglehelms <e.helms@cognigy.com>
Co-authored-by: Dave Horton <daveh@beachdognet.com>
2023-01-30 10:06:16 -05:00
Hoan Luu Huu
019599741a feat: add app_json to applications endpoint (#100)
* added phone_numbers.app_json column

* modify prev commit to put app_json on applications table

* feat: add app_json to applications endpoint

---------

Co-authored-by: Dave Horton <daveh@beachdognet.com>
Co-authored-by: Quan HL <quanluuhoang8@gmail.com>
2023-01-28 10:02:04 -05:00
EgleH
f2c2623b28 Speech should always be attached to an SP (#98)
* user will always be attached to SP, thus always provide SP sid

* add another fallback for service_provider_sid

* fix the email and username check in user creation that was crashing the server

* not allow same names for shared and account carriers

Co-authored-by: eglehelms <e.helms@cognigy.com>
2023-01-26 07:53:29 -05:00
20 changed files with 727 additions and 329 deletions

View File

@@ -6,10 +6,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: lts/*
- run: npm install
- run: npm run jslint
- run: npm test

View File

@@ -20,7 +20,7 @@ jobs:
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Build image
run: docker build . --file Dockerfile.db-create --tag $IMAGE_NAME

View File

@@ -20,7 +20,7 @@ jobs:
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Build image
run: docker build . --file Dockerfile --tag $IMAGE_NAME

View File

@@ -1,4 +1,4 @@
FROM --platform=linux/amd64 node:18.12.1-alpine3.16 as base
FROM --platform=linux/amd64 node:18-alpine3.16 as base
RUN apk --update --no-cache add --virtual .builds-deps build-base python3

View File

@@ -435,6 +435,7 @@ account_sid CHAR(36) COMMENT 'account that this application belongs to (if null,
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 ',
app_json TEXT,
speech_synthesis_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
speech_synthesis_language VARCHAR(12) NOT NULL DEFAULT 'en-US',
speech_synthesis_voice VARCHAR(64),
@@ -647,4 +648,4 @@ ALTER TABLE accounts ADD FOREIGN KEY device_calling_application_sid_idxfk (devic
ALTER TABLE accounts ADD FOREIGN KEY siprec_hook_sid_idxfk (siprec_hook_sid) REFERENCES applications (application_sid);
SET FOREIGN_KEY_CHECKS=1;
SET FOREIGN_KEY_CHECKS=1;

View File

@@ -1841,8 +1841,8 @@
<name><![CDATA[predefined_carriers]]></name>
<schema><![CDATA[]]></schema>
<location>
<x>47.00</x>
<y>1103.00</y>
<x>35.00</x>
<y>1194.00</y>
</location>
<size>
<width>302.00</width>
@@ -2187,7 +2187,7 @@
</location>
<size>
<width>345.00</width>
<height>300.00</height>
<height>320.00</height>
</size>
<zorder>0</zorder>
<SQLField>
@@ -2281,6 +2281,11 @@
<objectComment><![CDATA[webhook to call for inbound SMS/MMS ]]></objectComment>
<uid><![CDATA[4690A98A-2F67-4205-A5C1-D9523F6B3282]]></uid>
</SQLField>
<SQLField>
<name><![CDATA[app_json]]></name>
<type><![CDATA[TEXT]]></type>
<uid><![CDATA[B3BB24D3-DDA7-418F-A8CE-FD0E3E8B25C7]]></uid>
</SQLField>
<SQLField>
<name><![CDATA[speech_synthesis_vendor]]></name>
<type><![CDATA[VARCHAR(64)]]></type>
@@ -2711,10 +2716,10 @@
<SourceSidebarWidth><![CDATA[0.000000]]></SourceSidebarWidth>
<SQLEditorFileFormatVersion><![CDATA[4]]></SQLEditorFileFormatVersion>
<uid><![CDATA[58C99A00-06C9-478C-A667-C63842E088F3]]></uid>
<windowHeight><![CDATA[870.000000]]></windowHeight>
<windowHeight><![CDATA[917.000000]]></windowHeight>
<windowLocationX><![CDATA[0.000000]]></windowLocationX>
<windowLocationY><![CDATA[74.000000]]></windowLocationY>
<windowScrollOrigin><![CDATA[{432, 18.5}]]></windowScrollOrigin>
<windowLocationY><![CDATA[27.000000]]></windowLocationY>
<windowScrollOrigin><![CDATA[{383, 243.5}]]></windowScrollOrigin>
<windowWidth><![CDATA[1512.000000]]></windowWidth>
</SQLDocumentInfo>
<AllowsIndexRenamingOnInsert><![CDATA[1]]></AllowsIndexRenamingOnInsert>

View File

@@ -68,6 +68,9 @@ VALUES
('d2ccfcb1-9198-4fe9-a0ca-6e49395837c4', '7d509a18-bbff-4c5d-b21e-b99bf8f8c49a', '54.172.60.0', 30, 5060, 1, 0),
('6b1d0032-4430-41f1-87c6-f22233d394ef', '7d509a18-bbff-4c5d-b21e-b99bf8f8c49a', '54.244.51.0', 30, 5060, 1, 0),
('0de40217-8bd5-4aa8-a9fd-1994282953c6', '7d509a18-bbff-4c5d-b21e-b99bf8f8c49a', '54.171.127.192', 30, 5060, 1, 0),
('48b108e3-1ce7-4f18-a4cb-e41e63688bdf', '7d509a18-bbff-4c5d-b21e-b99bf8f8c49a', '54.171.127.193', 30, 5060, 1, 0),
('d9131a69-fe44-4c2a-ba82-4adc81f628dd', '7d509a18-bbff-4c5d-b21e-b99bf8f8c49a', '54.171.127.194', 30, 5060, 1, 0),
('34a6a311-4bd6-49ca-aa77-edd3cb92c6e1', '7d509a18-bbff-4c5d-b21e-b99bf8f8c49a', '54.171.127.195', 30, 5060, 1, 0),
('37bc0b20-b53c-4c31-95a6-f82b1c3713e3', '7d509a18-bbff-4c5d-b21e-b99bf8f8c49a', '35.156.191.128', 30, 5060, 1, 0),
('39791f4e-b612-4882-a37e-e92711a39f3f', '7d509a18-bbff-4c5d-b21e-b99bf8f8c49a', '54.65.63.192', 30, 5060, 1, 0),
('81a0c8cb-a33e-42da-8f20-99083da6f02f', '7d509a18-bbff-4c5d-b21e-b99bf8f8c49a', '54.252.254.64', 30, 5060, 1, 0),

View File

@@ -59,6 +59,7 @@ const sql = {
'ALTER TABLE `voip_carriers` ADD COLUMN `register_public_ip_in_contact` BOOLEAN NOT NULL DEFAULT false'
],
'8000': [
'ALTER TABLE `applications` ADD COLUMN `app_json` TEXT',
'ALTER TABLE voip_carriers CHANGE register_public_domain_in_contact register_public_ip_in_contact BOOLEAN',
'alter table phone_numbers modify number varchar(132) NOT NULL UNIQUE',
`CREATE TABLE permissions

View File

@@ -8,9 +8,7 @@ const short = require('short-uuid');
const {promisePool} = require('../../db');
const sysError = require('../error');
const sqlSelectCarrierByName = `SELECT * FROM voip_carriers
WHERE account_sid = ?
AND name = ?`;
const sqlSelectCarrierByNameForSP = `SELECT * FROM voip_carriers
WHERE service_provider_sid = ?
AND name = ?`;
@@ -25,18 +23,20 @@ router.post('/:sid', async(req, res) => {
const {sid } = req.params;
let service_provider_sid;
const {account_sid} = req.user;
if (!account_sid) {
service_provider_sid = parseServiceProviderSid(req);
} else {
service_provider_sid = req.user.service_provider_sid;
}
try {
const [template] = await PredefinedCarrier.retrieve(sid);
logger.debug({template}, `Retrieved template carrier for sid ${sid}`);
if (!template) return res.sendStatus(404);
/* make sure not to add the same carrier twice */
const [r2] = account_sid ?
await promisePool.query(sqlSelectCarrierByName, [account_sid, template.name]) :
await promisePool.query(sqlSelectCarrierByNameForSP, [service_provider_sid, template.name]);
const [r2] = await promisePool.query(sqlSelectCarrierByNameForSP, [service_provider_sid, template.name]);
if (r2.length > 0) {
template.name = `${template.name}-${short.generate()}`;

View File

@@ -6,6 +6,7 @@ const Webhook = require('../../models/webhook');
const {promisePool} = require('../../db');
const decorate = require('./decorate');
const sysError = require('../error');
const { validate } = require('@jambonz/verb-specifications');
const preconditions = {
'add': validateAdd,
'update': validateUpdate
@@ -76,6 +77,16 @@ router.post('/', async(req, res) => {
}
}
// validate app json if required
if (obj['app_json']) {
const app_json = JSON.parse(obj['app_json']);
try {
validate(logger, app_json);
} catch (err) {
throw new DbErrorBadRequest(err);
}
}
const uuid = await Application.make(obj);
res.status(201).json({sid: uuid});
} catch (err) {
@@ -179,6 +190,16 @@ router.put('/:sid', async(req, res) => {
delete obj[prop];
}
// validate app json if required
if (obj['app_json']) {
const app_json = JSON.parse(obj['app_json']);
try {
validate(logger, app_json);
} catch (err) {
throw new DbErrorBadRequest(err);
}
}
const rowsAffected = await Application.update(sid, obj);
if (rowsAffected === 0) {
return res.status(404).end();

View File

@@ -12,7 +12,7 @@ const {hasServiceProviderPermissions, parseServiceProviderSid} = require('./util
const sysError = require('../error');
const decorate = require('./decorate');
const preconditions = {
'delete': noActiveAccounts
'delete': noActiveAccountsOrUsers
};
const sqlDeleteSipGateways = `DELETE from sip_gateways
WHERE voip_carrier_sid IN (
@@ -59,12 +59,20 @@ function validateUpdate(req) {
}
/* can not delete a service provider if it has any active accounts */
async function noActiveAccounts(req, sid) {
async function noActiveAccountsOrUsers(req, sid) {
if (!req.user.hasAdminAuth) {
throw new DbErrorForbidden('only admin users can delete a service provider');
}
const activeAccounts = await ServiceProvider.getForeignKeyReferences('accounts.service_provider_sid', sid);
const activeUsers = await ServiceProvider.getForeignKeyReferences('users.service_provider_sid', sid);
if (activeAccounts > 0 && activeUsers > 0) throw new DbErrorUnprocessableRequest(
'cannot delete service provider with active accounts or users'
);
if (activeAccounts > 0) throw new DbErrorUnprocessableRequest('cannot delete service provider with active accounts');
if (activeUsers > 0) throw new DbErrorUnprocessableRequest(
'cannot delete service provider with active service provider level users'
);
/* ok we can delete -- no active accounts. remove carriers and speech credentials */
await promisePool.execute('DELETE from speech_credentials WHERE service_provider_sid = ?', [sid]);

View File

@@ -1,5 +1,6 @@
const router = require('express').Router();
const assert = require('assert');
const Account = require('../../models/account');
const SpeechCredential = require('../../models/speech-credential');
const sysError = require('../error');
const {decrypt, encrypt} = require('../../utils/encrypt-decrypt');
@@ -50,6 +51,7 @@ const encryptCredential = (obj) => {
tts_region,
stt_api_key,
stt_region,
riva_server_uri,
instance_id
} = obj;
@@ -105,6 +107,11 @@ const encryptCredential = (obj) => {
const ibmData = JSON.stringify({tts_api_key, tts_region, stt_api_key, stt_region, instance_id});
return encrypt(ibmData);
case 'nvidia':
assert(riva_server_uri, 'invalid riva server uri: riva_server_uri is required');
const nvidiaData = JSON.stringify({ riva_server_uri });
return encrypt(nvidiaData);
default:
assert(false, `invalid or missing vendor: ${vendor}`);
}
@@ -118,13 +125,13 @@ router.post('/', async(req, res) => {
vendor,
} = req.body;
const account_sid = req.user.account_sid || req.body.account_sid;
let service_provider_sid;
const service_provider_sid = req.user.service_provider_sid ||
req.body.service_provider_sid || parseServiceProviderSid(req);
if (!account_sid) {
if (!req.user.hasServiceProviderAuth && !req.user.hasAdminAuth) {
logger.error('POST /SpeechCredentials invalid credentials');
return res.sendStatus(403);
}
service_provider_sid = parseServiceProviderSid(req);
}
try {
const encrypted_credential = encryptCredential(req.body);
@@ -146,15 +153,17 @@ router.post('/', async(req, res) => {
* retrieve all speech credentials for an account
*/
router.get('/', async(req, res) => {
let service_provider_sid;
const account_sid = parseAccountSid(req);
if (!account_sid) service_provider_sid = parseServiceProviderSid(req);
const account_sid = parseAccountSid(req) || req.user.account_sid;
const service_provider_sid = parseServiceProviderSid(req) || req.user.service_provider_sid;
const logger = req.app.locals.logger;
try {
let creds = account_sid ?
await SpeechCredential.retrieveAll(account_sid) :
await SpeechCredential.retrieveAllForSP(service_provider_sid);
const credsAccount = account_sid ? await SpeechCredential.retrieveAll(account_sid) : [];
const credsSP = service_provider_sid ?
await SpeechCredential.retrieveAllForSP(service_provider_sid) :
await SpeechCredential.retrieveAllForSP((await Account.retrieve(account_sid))[0].service_provider_sid);
// filter out duplicates and discard those from other non-matching accounts
let creds = [...new Set([...credsAccount, ...credsSP].map((c) => JSON.stringify(c)))].map((c) => JSON.parse(c));
if (req.user.hasScope('account')) {
creds = creds.filter((c) => c.account_sid === req.user.account_sid || !c.account_sid);
}
@@ -207,6 +216,9 @@ router.get('/', async(req, res) => {
obj.stt_api_key = obscureKey(o.stt_api_key);
obj.stt_region = o.stt_region;
obj.instance_id = o.instance_id;
} else if ('nvidia' == obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.riva_server_uri = o.riva_server_uri;
}
return obj;
}));
@@ -269,6 +281,9 @@ router.get('/:sid', async(req, res) => {
obj.stt_api_key = obscureKey(o.stt_api_key);
obj.stt_region = o.stt_region;
obj.instance_id = o.instance_id;
} else if ('nvidia' == obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.riva_server_uri = o.riva_server_uri;
}
res.status(200).json(obj);
} catch (err) {
@@ -299,7 +314,7 @@ router.put('/:sid', async(req, res) => {
const sid = req.params.sid;
const logger = req.app.locals.logger;
try {
const {use_for_tts, use_for_stt, region, aws_region, stt_region, tts_region} = req.body;
const {use_for_tts, use_for_stt, region, aws_region, stt_region, tts_region, riva_server_uri} = req.body;
if (typeof use_for_tts === 'undefined' && typeof use_for_stt === 'undefined') {
throw new DbErrorUnprocessableRequest('use_for_tts and use_for_stt are the only updateable fields');
}
@@ -334,7 +349,8 @@ router.put('/:sid', async(req, res) => {
use_custom_stt,
custom_stt_endpoint,
stt_region,
tts_region
tts_region,
riva_server_uri
};
logger.info({o, newCred}, 'updating speech credential with this new credential');
obj.credential = encryptCredential(newCred);

View File

@@ -373,9 +373,16 @@ router.post('/', async(req, res) => {
try {
const email = allUsers.find((e) => e.email === payload.email);
const name = allUsers.find((e) => e.name === payload.name);
if (name) {
logger.debug({payload}, 'user with this username already exists');
return res.status(422).json({msg: 'user with this username already exists'});
}
if (email) {
logger.debug({payload}, 'user with this email already exists');
res.status(422).json({msg: 'user with this email already exists'});
return res.status(422).json({msg: 'user with this email already exists'});
}
if (decodedJwt.scope === 'admin') {
@@ -417,7 +424,8 @@ router.delete('/:user_sid', async(req, res) => {
const user = await User.retrieve(user_sid);
try {
if (decodedJwt.scope === 'admin' && activeAdminUsers.length === 1) {
if (decodedJwt.scope === 'admin' && !user.account_sid && !user.service_provider_sid &&
activeAdminUsers.length === 1) {
throw new Error('cannot delete this admin user - there are no other active admin users');
}

View File

@@ -3382,6 +3382,9 @@ paths:
messaging_hook:
$ref: '#/components/schemas/Webhook'
description: application webhook to handle inbound SMS/MMS messages
app_json:
type: string
description: Voice Application Json, call_hook will not be invoked if app_json is provided
speech_synthesis_vendor:
type: string
speech_synthesis_voice:
@@ -4259,6 +4262,8 @@ components:
type: boolean
stt_tested_ok:
type: boolean
riva_server_uri:
type: string
SpeechCredentialUpdate:
properties:
use_for_tts:

View File

@@ -16,7 +16,6 @@ const testNuanceStt = async(logger, credentials) => {
return true;
};
const testGoogleTts = async(logger, credentials) => {
const client = new ttsGoogle.TextToSpeechClient({credentials});
await client.listVoices();

778
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,15 +24,15 @@
"@jambonz/db-helpers": "^0.7.3",
"@jambonz/realtimedb-helpers": "^0.6.0",
"@jambonz/time-series": "^0.2.5",
"@jambonz/verb-specifications": "^0.0.3",
"argon2-ffi": "^2.0.0",
"aws-sdk": "^2.1152.0",
"aws-sdk": "^2.1302.0",
"bent": "^7.3.12",
"cors": "^2.8.5",
"debug": "^4.3.4",
"express": "^4.18.1",
"express-rate-limit": "^6.4.0",
"form-data": "^2.5.1",
"form-urlencoded": "^6.1.0",
"helmet": "^5.1.0",
"ibm-watson": "^7.1.2",
"jsonwebtoken": "^9.0.0",

View File

@@ -23,6 +23,37 @@ test('application tests', async(t) => {
const service_provider_sid = await createServiceProvider(request);
const phone_number_sid = await createPhoneNumber(request, voip_carrier_sid);
const account_sid = await createAccount(request, service_provider_sid);
/* add an invalid application app_json */
result = await request.post('/Applications', {
resolveWithFullResponse: true,
simple: false,
auth: authAdmin,
json: true,
body: {
name: 'daveh',
account_sid,
call_hook: {
url: 'http://example.com'
},
call_status_hook: {
url: 'http://example.com/status',
method: 'POST'
},
messaging_hook: {
url: 'http://example.com/sms'
},
app_json : '[\
{\
"verb": "play",\
"timeoutSecs": 10,\
"seekOffset": 8000,\
"actionHook": "/play/action"\
}\
]'
}
});
t.ok(result.statusCode === 400, 'Cant create application with invalid app_josn');
/* add an application */
result = await request.post('/Applications', {
@@ -41,7 +72,16 @@ test('application tests', async(t) => {
},
messaging_hook: {
url: 'http://example.com/sms'
}
},
app_json : '[\
{\
"verb": "play",\
"url": "https://example.com/example.mp3",\
"timeoutSecs": 10,\
"seekOffset": 8000,\
"actionHook": "/play/action"\
}\
]'
}
});
t.ok(result.statusCode === 201, 'successfully created application');
@@ -62,6 +102,9 @@ test('application tests', async(t) => {
});
t.ok(result.name === 'daveh' , 'successfully retrieved application by sid');
t.ok(result.messaging_hook.url === 'http://example.com/sms' , 'successfully retrieved messaging_hook from application');
let app_json = JSON.parse(result.app_json);
t.ok(app_json[0].verb === 'play', 'successfully retrieved app_json from application')
/* update applications */
result = await request.put(`/Applications/${sid}`, {
@@ -74,7 +117,15 @@ test('application tests', async(t) => {
},
messaging_hook: {
url: 'http://example2.com/mms'
}
},
app_json : '[\
{\
"verb": "hangup",\
"headers": {\
"X-Reason" : "maximum call duration exceeded"\
}\
}\
]'
}
});
t.ok(result.statusCode === 204, 'successfully updated application');
@@ -85,6 +136,57 @@ test('application tests', async(t) => {
json: true,
});
t.ok(result.messaging_hook.url === 'http://example2.com/mms' , 'successfully updated messaging_hook');
app_json = JSON.parse(result.app_json);
t.ok(app_json[0].verb === 'hangup', 'successfully updated app_json from application')
/* remove applications app_json*/
result = await request.put(`/Applications/${sid}`, {
auth: authAdmin,
json: true,
resolveWithFullResponse: true,
body: {
call_hook: {
url: 'http://example2.com'
},
messaging_hook: {
url: 'http://example2.com/mms'
},
app_json : null
}
});
t.ok(result.statusCode === 204, 'successfully updated application');
/* validate messaging hook was updated */
result = await request.get(`/Applications/${sid}`, {
auth: authAdmin,
json: true,
});
t.ok(result.app_json == undefined, 'successfully removed app_json from application')
/* Update invalid applications app_json*/
result = await request.put(`/Applications/${sid}`, {
auth: authAdmin,
json: true,
resolveWithFullResponse: true,
simple: false,
body: {
call_hook: {
url: 'http://example2.com'
},
messaging_hook: {
url: 'http://example2.com/mms'
},
app_json : '[\
{\
"verb": "play",\
"timeoutSecs": 10,\
"seekOffset": 8000,\
"actionHook": "/play/action"\
}\
]'
}
});
t.ok(result.statusCode === 400, 'Cant update invalid application app_json');
/* assign phone number to application */
result = await request.put(`/PhoneNumbers/${phone_number_sid}`, {

View File

@@ -86,7 +86,7 @@ test('service provider tests', async(t) => {
auth: authAdmin,
json: true,
});
console.log(JSON.stringify(result));
//console.log(JSON.stringify(result));
t.ok(result.length === 2 , 'successfully queried all service providers');
/* query one service providers */

View File

@@ -300,6 +300,37 @@ test('speech credentials tests', async(t) => {
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
}
/* add a credential for nvidia */
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
vendor: 'nvidia',
use_for_stt: true,
use_for_tts: true,
riva_server_uri: "192.168.1.2:5060"
}
});
t.ok(result.statusCode === 201, 'successfully added speech credential for nvidia');
const ms_sid = result.body.sid;
/* test the speech credential */
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/${ms_sid}/test`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
});
// TODO Nvidia test.
t.ok(result.statusCode === 200 && result.body.stt.status === 'not tested', 'successfully tested speech credential for nvida stt');
/* delete the credential */
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${ms_sid}`, {
auth: authUser,
resolveWithFullResponse: true,
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
await deleteObjectBySid(request, '/Accounts', account_sid);
await deleteObjectBySid(request, '/ServiceProviders', service_provider_sid);
//t.end();