mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-01-25 02:08:24 +00:00
feat support multi speech credential with diff labels and same vendor (#218)
* feat support multi speech credential with diff labels and same vendor * fix review comment * wip * fix review comments * update verb spec version
This commit is contained in:
@@ -334,6 +334,7 @@ last_tested DATETIME,
|
|||||||
tts_tested_ok BOOLEAN,
|
tts_tested_ok BOOLEAN,
|
||||||
stt_tested_ok BOOLEAN,
|
stt_tested_ok BOOLEAN,
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
label VARCHAR(64),
|
||||||
PRIMARY KEY (speech_credential_sid)
|
PRIMARY KEY (speech_credential_sid)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -478,8 +479,10 @@ app_json TEXT,
|
|||||||
speech_synthesis_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
speech_synthesis_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||||
speech_synthesis_language VARCHAR(12) NOT NULL DEFAULT 'en-US',
|
speech_synthesis_language VARCHAR(12) NOT NULL DEFAULT 'en-US',
|
||||||
speech_synthesis_voice VARCHAR(64),
|
speech_synthesis_voice VARCHAR(64),
|
||||||
|
speech_synthesis_label VARCHAR(64),
|
||||||
speech_recognizer_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
speech_recognizer_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||||
speech_recognizer_language VARCHAR(64) NOT NULL DEFAULT 'en-US',
|
speech_recognizer_language VARCHAR(64) NOT NULL DEFAULT 'en-US',
|
||||||
|
speech_recognizer_label VARCHAR(64),
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
record_all_calls BOOLEAN NOT NULL DEFAULT false,
|
record_all_calls BOOLEAN NOT NULL DEFAULT false,
|
||||||
PRIMARY KEY (application_sid)
|
PRIMARY KEY (application_sid)
|
||||||
@@ -609,8 +612,6 @@ CREATE INDEX smpp_address_sid_idx ON smpp_addresses (smpp_address_sid);
|
|||||||
CREATE INDEX service_provider_sid_idx ON smpp_addresses (service_provider_sid);
|
CREATE INDEX service_provider_sid_idx ON smpp_addresses (service_provider_sid);
|
||||||
ALTER TABLE smpp_addresses ADD FOREIGN KEY service_provider_sid_idxfk_4 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
ALTER TABLE smpp_addresses ADD FOREIGN KEY service_provider_sid_idxfk_4 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX speech_credentials_idx_1 ON speech_credentials (vendor,account_sid);
|
|
||||||
|
|
||||||
CREATE INDEX speech_credential_sid_idx ON speech_credentials (speech_credential_sid);
|
CREATE INDEX speech_credential_sid_idx ON speech_credentials (speech_credential_sid);
|
||||||
CREATE INDEX service_provider_sid_idx ON speech_credentials (service_provider_sid);
|
CREATE INDEX service_provider_sid_idx ON speech_credentials (service_provider_sid);
|
||||||
ALTER TABLE speech_credentials ADD FOREIGN KEY service_provider_sid_idxfk_5 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
ALTER TABLE speech_credentials ADD FOREIGN KEY service_provider_sid_idxfk_5 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||||
|
|||||||
@@ -884,7 +884,7 @@
|
|||||||
<y>958.00</y>
|
<y>958.00</y>
|
||||||
</location>
|
</location>
|
||||||
<size>
|
<size>
|
||||||
<width>368.00</width>
|
<width>302.00</width>
|
||||||
<height>280.00</height>
|
<height>280.00</height>
|
||||||
</size>
|
</size>
|
||||||
<zorder>14</zorder>
|
<zorder>14</zorder>
|
||||||
@@ -981,24 +981,11 @@
|
|||||||
<notNull><![CDATA[1]]></notNull>
|
<notNull><![CDATA[1]]></notNull>
|
||||||
<uid><![CDATA[8860648C-4790-4A01-9E2E-60DC52A287FA]]></uid>
|
<uid><![CDATA[8860648C-4790-4A01-9E2E-60DC52A287FA]]></uid>
|
||||||
</SQLField>
|
</SQLField>
|
||||||
<SQLIndex>
|
<SQLField>
|
||||||
<name><![CDATA[speech_credentials_idx_1]]></name>
|
<name><![CDATA[label]]></name>
|
||||||
<fieldName><![CDATA[vendor]]></fieldName>
|
<type><![CDATA[VARCHAR(64)]]></type>
|
||||||
<fieldName><![CDATA[account_sid]]></fieldName>
|
<uid><![CDATA[0D42A22C-DF14-42A1-BDE2-A53AC8B0D8D6]]></uid>
|
||||||
<SQLIndexEntry>
|
</SQLField>
|
||||||
<name><![CDATA[vendor]]></name>
|
|
||||||
<prefixSize><![CDATA[]]></prefixSize>
|
|
||||||
<fieldUid><![CDATA[9D8FCF55-D68E-44D3-90DF-27B5ABD1D0BE]]></fieldUid>
|
|
||||||
</SQLIndexEntry>
|
|
||||||
<SQLIndexEntry>
|
|
||||||
<name><![CDATA[account_sid]]></name>
|
|
||||||
<prefixSize><![CDATA[]]></prefixSize>
|
|
||||||
<fieldUid><![CDATA[7E964ED2-EC2E-4BCB-8DEC-C455B87FAC07]]></fieldUid>
|
|
||||||
</SQLIndexEntry>
|
|
||||||
<indexNamePrefix><![CDATA[speech_credentials]]></indexNamePrefix>
|
|
||||||
<indexType><![CDATA[UNIQUE]]></indexType>
|
|
||||||
<uid><![CDATA[554ABEC2-3E1B-41B1-BF07-25F403D5E3B4]]></uid>
|
|
||||||
</SQLIndex>
|
|
||||||
<labelWindowIndex><![CDATA[21]]></labelWindowIndex>
|
<labelWindowIndex><![CDATA[21]]></labelWindowIndex>
|
||||||
<ui.treeExpanded><![CDATA[1]]></ui.treeExpanded>
|
<ui.treeExpanded><![CDATA[1]]></ui.treeExpanded>
|
||||||
<uid><![CDATA[49A68E1C-DEE2-446C-A4EB-9850E16155CC]]></uid>
|
<uid><![CDATA[49A68E1C-DEE2-446C-A4EB-9850E16155CC]]></uid>
|
||||||
@@ -2368,7 +2355,7 @@
|
|||||||
</location>
|
</location>
|
||||||
<size>
|
<size>
|
||||||
<width>345.00</width>
|
<width>345.00</width>
|
||||||
<height>340.00</height>
|
<height>380.00</height>
|
||||||
</size>
|
</size>
|
||||||
<zorder>0</zorder>
|
<zorder>0</zorder>
|
||||||
<SQLField>
|
<SQLField>
|
||||||
@@ -2487,6 +2474,12 @@
|
|||||||
<notNull><![CDATA[0]]></notNull>
|
<notNull><![CDATA[0]]></notNull>
|
||||||
<uid><![CDATA[929D66F0-64B9-4D7C-AB4B-24F131E1178F]]></uid>
|
<uid><![CDATA[929D66F0-64B9-4D7C-AB4B-24F131E1178F]]></uid>
|
||||||
</SQLField>
|
</SQLField>
|
||||||
|
<SQLField>
|
||||||
|
<name><![CDATA[speech_synthesis_label]]></name>
|
||||||
|
<type><![CDATA[VARCHAR(64)]]></type>
|
||||||
|
<notNull><![CDATA[0]]></notNull>
|
||||||
|
<uid><![CDATA[BFA24DF2-9CF5-47B0-848D-8B685B7C6750]]></uid>
|
||||||
|
</SQLField>
|
||||||
<SQLField>
|
<SQLField>
|
||||||
<name><![CDATA[speech_recognizer_vendor]]></name>
|
<name><![CDATA[speech_recognizer_vendor]]></name>
|
||||||
<type><![CDATA[VARCHAR(64)]]></type>
|
<type><![CDATA[VARCHAR(64)]]></type>
|
||||||
@@ -2501,6 +2494,12 @@
|
|||||||
<notNull><![CDATA[1]]></notNull>
|
<notNull><![CDATA[1]]></notNull>
|
||||||
<uid><![CDATA[A03AFB7B-492F-48E3-AE3C-B1416D5B6B12]]></uid>
|
<uid><![CDATA[A03AFB7B-492F-48E3-AE3C-B1416D5B6B12]]></uid>
|
||||||
</SQLField>
|
</SQLField>
|
||||||
|
<SQLField>
|
||||||
|
<name><![CDATA[speech_recognizer_label]]></name>
|
||||||
|
<type><![CDATA[VARCHAR(64)]]></type>
|
||||||
|
<notNull><![CDATA[0]]></notNull>
|
||||||
|
<uid><![CDATA[A247A784-CCD6-40B4-9D0A-2F0EF8F8AFD2]]></uid>
|
||||||
|
</SQLField>
|
||||||
<SQLField>
|
<SQLField>
|
||||||
<name><![CDATA[created_at]]></name>
|
<name><![CDATA[created_at]]></name>
|
||||||
<type><![CDATA[DATETIME]]></type>
|
<type><![CDATA[DATETIME]]></type>
|
||||||
@@ -2966,9 +2965,9 @@
|
|||||||
<SQLEditorFileFormatVersion><![CDATA[4]]></SQLEditorFileFormatVersion>
|
<SQLEditorFileFormatVersion><![CDATA[4]]></SQLEditorFileFormatVersion>
|
||||||
<uid><![CDATA[58C99A00-06C9-478C-A667-C63842E088F3]]></uid>
|
<uid><![CDATA[58C99A00-06C9-478C-A667-C63842E088F3]]></uid>
|
||||||
<windowHeight><![CDATA[1055.000000]]></windowHeight>
|
<windowHeight><![CDATA[1055.000000]]></windowHeight>
|
||||||
<windowLocationX><![CDATA[0.000000]]></windowLocationX>
|
<windowLocationX><![CDATA[1728.000000]]></windowLocationX>
|
||||||
<windowLocationY><![CDATA[24.000000]]></windowLocationY>
|
<windowLocationY><![CDATA[37.000000]]></windowLocationY>
|
||||||
<windowScrollOrigin><![CDATA[{157, 832}]]></windowScrollOrigin>
|
<windowScrollOrigin><![CDATA[{157, 830}]]></windowScrollOrigin>
|
||||||
<windowWidth><![CDATA[1682.000000]]></windowWidth>
|
<windowWidth><![CDATA[1682.000000]]></windowWidth>
|
||||||
</SQLDocumentInfo>
|
</SQLDocumentInfo>
|
||||||
<AllowsIndexRenamingOnInsert><![CDATA[1]]></AllowsIndexRenamingOnInsert>
|
<AllowsIndexRenamingOnInsert><![CDATA[1]]></AllowsIndexRenamingOnInsert>
|
||||||
|
|||||||
@@ -159,6 +159,12 @@ const sql = {
|
|||||||
'CREATE INDEX client_sid_idx ON clients (client_sid)',
|
'CREATE INDEX client_sid_idx ON clients (client_sid)',
|
||||||
'ALTER TABLE clients ADD CONSTRAINT account_sid_idxfk_13 FOREIGN KEY account_sid_idxfk_13 (account_sid) REFERENCES accounts (account_sid)',
|
'ALTER TABLE clients ADD CONSTRAINT account_sid_idxfk_13 FOREIGN KEY account_sid_idxfk_13 (account_sid) REFERENCES accounts (account_sid)',
|
||||||
'ALTER TABLE sip_gateways ADD COLUMN protocol ENUM(\'udp\',\'tcp\',\'tls\', \'tls/srtp\') DEFAULT \'udp\''
|
'ALTER TABLE sip_gateways ADD COLUMN protocol ENUM(\'udp\',\'tcp\',\'tls\', \'tls/srtp\') DEFAULT \'udp\''
|
||||||
|
],
|
||||||
|
8005: [
|
||||||
|
'DROP INDEX speech_credentials_idx_1 ON speech_credentials',
|
||||||
|
'ALTER TABLE speech_credentials ADD COLUMN label VARCHAR(64)',
|
||||||
|
'ALTER TABLE applications ADD COLUMN speech_synthesis_label VARCHAR(64)',
|
||||||
|
'ALTER TABLE applications ADD COLUMN speech_recognizer_label VARCHAR(64)'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -190,6 +196,7 @@ const doIt = async() => {
|
|||||||
if (val < 8000) upgrades.push(...sql['8000']);
|
if (val < 8000) upgrades.push(...sql['8000']);
|
||||||
if (val < 8003) upgrades.push(...sql['8003']);
|
if (val < 8003) upgrades.push(...sql['8003']);
|
||||||
if (val < 8004) upgrades.push(...sql['8004']);
|
if (val < 8004) upgrades.push(...sql['8004']);
|
||||||
|
if (val < 8005) upgrades.push(...sql['8005']);
|
||||||
|
|
||||||
// perform all upgrades
|
// perform all upgrades
|
||||||
logger.info({upgrades}, 'applying schema upgrades..');
|
logger.info({upgrades}, 'applying schema upgrades..');
|
||||||
|
|||||||
@@ -20,6 +20,17 @@ class SpeechCredential extends Model {
|
|||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async isAvailableVendorAndLabel(service_provider_sid, account_sid, vendor, label) {
|
||||||
|
let sql;
|
||||||
|
if (account_sid) {
|
||||||
|
sql = 'SELECT * FROM speech_credentials WHERE account_sid = ? AND vendor = ? AND label = ?';
|
||||||
|
} else {
|
||||||
|
sql = 'SELECT * FROM speech_credentials WHERE service_provider_sid = ? AND vendor = ? AND label = ?';
|
||||||
|
}
|
||||||
|
const [rows] = await promisePool.query(sql, [account_sid ? account_sid : service_provider_sid, vendor, label]);
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
static async disableStt(account_sid) {
|
static async disableStt(account_sid) {
|
||||||
await promisePool.execute('UPDATE speech_credentials SET use_for_stt = 0 WHERE account_sid = ?', [account_sid]);
|
await promisePool.execute('UPDATE speech_credentials SET use_for_stt = 0 WHERE account_sid = ?', [account_sid]);
|
||||||
}
|
}
|
||||||
@@ -86,6 +97,10 @@ SpeechCredential.fields = [
|
|||||||
{
|
{
|
||||||
name: 'last_tested',
|
name: 'last_tested',
|
||||||
type: 'date'
|
type: 'date'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'label',
|
||||||
|
type: 'string'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ router.post('/', async(req, res) => {
|
|||||||
use_for_stt,
|
use_for_stt,
|
||||||
use_for_tts,
|
use_for_tts,
|
||||||
vendor,
|
vendor,
|
||||||
|
label
|
||||||
} = req.body;
|
} = req.body;
|
||||||
const account_sid = req.user.account_sid || req.body.account_sid;
|
const account_sid = req.user.account_sid || req.body.account_sid;
|
||||||
const service_provider_sid = req.user.service_provider_sid ||
|
const service_provider_sid = req.user.service_provider_sid ||
|
||||||
@@ -221,11 +222,21 @@ router.post('/', async(req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if vendor and label is already used for account or SP
|
||||||
|
if (label) {
|
||||||
|
const existingSpeech = await SpeechCredential.isAvailableVendorAndLabel(
|
||||||
|
service_provider_sid, account_sid, vendor, label);
|
||||||
|
if (existingSpeech.length > 0) {
|
||||||
|
throw new DbErrorUnprocessableRequest(`Label ${label} is already in use for another speech credential`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const encrypted_credential = encryptCredential(req.body);
|
const encrypted_credential = encryptCredential(req.body);
|
||||||
const uuid = await SpeechCredential.make({
|
const uuid = await SpeechCredential.make({
|
||||||
account_sid,
|
account_sid,
|
||||||
service_provider_sid,
|
service_provider_sid,
|
||||||
vendor,
|
vendor,
|
||||||
|
label,
|
||||||
use_for_tts,
|
use_for_tts,
|
||||||
use_for_stt,
|
use_for_stt,
|
||||||
credential: encrypted_credential
|
credential: encrypted_credential
|
||||||
|
|||||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -19,7 +19,7 @@
|
|||||||
"@jambonz/realtimedb-helpers": "^0.8.6",
|
"@jambonz/realtimedb-helpers": "^0.8.6",
|
||||||
"@jambonz/speech-utils": "^0.0.15",
|
"@jambonz/speech-utils": "^0.0.15",
|
||||||
"@jambonz/time-series": "^0.2.8",
|
"@jambonz/time-series": "^0.2.8",
|
||||||
"@jambonz/verb-specifications": "^0.0.26",
|
"@jambonz/verb-specifications": "^0.0.27",
|
||||||
"@soniox/soniox-node": "^1.1.1",
|
"@soniox/soniox-node": "^1.1.1",
|
||||||
"argon2": "^0.30.3",
|
"argon2": "^0.30.3",
|
||||||
"bent": "^7.3.12",
|
"bent": "^7.3.12",
|
||||||
@@ -1906,9 +1906,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jambonz/verb-specifications": {
|
"node_modules/@jambonz/verb-specifications": {
|
||||||
"version": "0.0.26",
|
"version": "0.0.27",
|
||||||
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.26.tgz",
|
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.27.tgz",
|
||||||
"integrity": "sha512-C/2KpX7dLPrEOFbcpyjJ3FkR8EEp+QbNmJoWbCcfYoZEyLiOcawWVwPRvz8hNPVa/Hf2Scth9OvjKeGuny33gQ==",
|
"integrity": "sha512-DIcxhCNrgr2RTE3YrGNP15RqKyV+P8f97SPBlKd2zTM5aN2oV5xv+pRDx5gLzmrUZ5TIEaBXQN3vTmM2Zx5Q6g==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"pino": "^8.8.0"
|
"pino": "^8.8.0"
|
||||||
@@ -11017,9 +11017,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@jambonz/verb-specifications": {
|
"@jambonz/verb-specifications": {
|
||||||
"version": "0.0.26",
|
"version": "0.0.27",
|
||||||
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.26.tgz",
|
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.27.tgz",
|
||||||
"integrity": "sha512-C/2KpX7dLPrEOFbcpyjJ3FkR8EEp+QbNmJoWbCcfYoZEyLiOcawWVwPRvz8hNPVa/Hf2Scth9OvjKeGuny33gQ==",
|
"integrity": "sha512-DIcxhCNrgr2RTE3YrGNP15RqKyV+P8f97SPBlKd2zTM5aN2oV5xv+pRDx5gLzmrUZ5TIEaBXQN3vTmM2Zx5Q6g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"pino": "^8.8.0"
|
"pino": "^8.8.0"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"@jambonz/realtimedb-helpers": "^0.8.6",
|
"@jambonz/realtimedb-helpers": "^0.8.6",
|
||||||
"@jambonz/speech-utils": "^0.0.15",
|
"@jambonz/speech-utils": "^0.0.15",
|
||||||
"@jambonz/time-series": "^0.2.8",
|
"@jambonz/time-series": "^0.2.8",
|
||||||
"@jambonz/verb-specifications": "^0.0.26",
|
"@jambonz/verb-specifications": "^0.0.27",
|
||||||
"@jambonz/lamejs": "^1.2.2",
|
"@jambonz/lamejs": "^1.2.2",
|
||||||
"@soniox/soniox-node": "^1.1.1",
|
"@soniox/soniox-node": "^1.1.1",
|
||||||
"argon2": "^0.30.3",
|
"argon2": "^0.30.3",
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ test('speech credentials tests', async(t) => {
|
|||||||
json: true,
|
json: true,
|
||||||
body: {
|
body: {
|
||||||
vendor: 'google',
|
vendor: 'google',
|
||||||
|
label: 'label1',
|
||||||
service_key: jsonKey,
|
service_key: jsonKey,
|
||||||
use_for_tts: true,
|
use_for_tts: true,
|
||||||
use_for_stt: true
|
use_for_stt: true
|
||||||
@@ -111,6 +112,7 @@ test('speech credentials tests', async(t) => {
|
|||||||
json: true,
|
json: true,
|
||||||
});
|
});
|
||||||
t.ok(result.vendor === 'google' , 'successfully retrieved speech credential by sid');
|
t.ok(result.vendor === 'google' , 'successfully retrieved speech credential by sid');
|
||||||
|
t.ok(result.label === 'label1' , 'label is successfully created');
|
||||||
|
|
||||||
/* query all credentials */
|
/* query all credentials */
|
||||||
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials`, {
|
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials`, {
|
||||||
|
|||||||
Reference in New Issue
Block a user