@unboundcx/sdk 2.8.6 → 2.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -1
- package/base.js +43 -12
- package/index.js +27 -3
- package/package.json +5 -2
- package/proto/transcription.proto +207 -0
- package/services/ai/SttStream.js +311 -0
- package/services/ai/playbooks.js +958 -0
- package/services/ai.js +1006 -45
- package/services/engagementMetrics.js +6 -2
- package/services/externalOAuth.js +45 -0
- package/services/fax.js +249 -0
- package/services/knowledgeBase.js +229 -0
- package/services/messaging/EmailTemplatesService.js +31 -11
- package/services/messaging/TenDlcCampaignManagementService.js +187 -105
- package/services/messaging/TollFreeCampaignsService.js +263 -110
- package/services/notes.js +27 -2
- package/services/objects.js +92 -3
- package/services/phoneNumbers.js +88 -3
- package/services/portals.js +89 -0
- package/services/sipEndpoints.js +105 -33
- package/services/storage.js +310 -6
- package/services/taskRouter/MetricsService.js +111 -0
- package/services/taskRouter/TaskRouterService.js +12 -0
- package/services/taskRouter/TaskService.js +846 -0
- package/services/taskRouter/WorkerService.js +394 -0
- package/services/taskRouter.js +6 -0
- package/services/video.js +145 -5
- package/services/voice.js +124 -67
- package/services/workflows.js +167 -7
package/services/storage.js
CHANGED
|
@@ -213,8 +213,25 @@ export class StorageService {
|
|
|
213
213
|
formFields,
|
|
214
214
|
endpoint = '/storage/upload',
|
|
215
215
|
method = 'POST',
|
|
216
|
+
onProgress = null,
|
|
217
|
+
skipClamscan = false,
|
|
216
218
|
) {
|
|
217
219
|
const isNode = typeof window === 'undefined';
|
|
220
|
+
|
|
221
|
+
// In browser with progress callback: Use XMLHttpRequest
|
|
222
|
+
if (!isNode && onProgress && typeof onProgress === 'function') {
|
|
223
|
+
return this._performUploadWithProgress(
|
|
224
|
+
file,
|
|
225
|
+
fileName,
|
|
226
|
+
formFields,
|
|
227
|
+
endpoint,
|
|
228
|
+
method,
|
|
229
|
+
onProgress,
|
|
230
|
+
skipClamscan,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Default behavior: Use fetch via sdk._fetch
|
|
218
235
|
let formData, headers;
|
|
219
236
|
|
|
220
237
|
if (isNode) {
|
|
@@ -227,6 +244,15 @@ export class StorageService {
|
|
|
227
244
|
headers = result.headers;
|
|
228
245
|
}
|
|
229
246
|
|
|
247
|
+
if (process?.env?.AUTH_V3_TOKEN_TYPE_OVERRIDE) {
|
|
248
|
+
headers['x-token-type-override'] =
|
|
249
|
+
process.env.AUTH_V3_TOKEN_TYPE_OVERRIDE;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (skipClamscan && process?.env?.CLAMSCAN_OVERRIDE_KEY) {
|
|
253
|
+
headers['x-clamscan-override-key'] = process.env.CLAMSCAN_OVERRIDE_KEY;
|
|
254
|
+
}
|
|
255
|
+
|
|
230
256
|
const params = {
|
|
231
257
|
body: formData,
|
|
232
258
|
headers,
|
|
@@ -235,6 +261,145 @@ export class StorageService {
|
|
|
235
261
|
return await this.sdk._fetch(endpoint, method, params, true);
|
|
236
262
|
}
|
|
237
263
|
|
|
264
|
+
// Upload with progress tracking using XMLHttpRequest
|
|
265
|
+
async _performUploadWithProgress(
|
|
266
|
+
file,
|
|
267
|
+
fileName,
|
|
268
|
+
formFields,
|
|
269
|
+
endpoint,
|
|
270
|
+
method,
|
|
271
|
+
onProgress,
|
|
272
|
+
skipClamscan = false,
|
|
273
|
+
) {
|
|
274
|
+
const { formData } = this._createBrowserFormData(
|
|
275
|
+
file,
|
|
276
|
+
fileName,
|
|
277
|
+
formFields,
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
return new Promise((resolve, reject) => {
|
|
281
|
+
const xhr = new XMLHttpRequest();
|
|
282
|
+
|
|
283
|
+
const startTime = Date.now();
|
|
284
|
+
|
|
285
|
+
// Progress tracking
|
|
286
|
+
xhr.upload.onprogress = (event) => {
|
|
287
|
+
if (event.lengthComputable) {
|
|
288
|
+
const percentComplete = (event.loaded / event.total) * 100;
|
|
289
|
+
const elapsed = (Date.now() - startTime) / 1000; // seconds
|
|
290
|
+
const speed = event.loaded / elapsed; // bytes per second
|
|
291
|
+
|
|
292
|
+
onProgress({
|
|
293
|
+
loaded: event.loaded,
|
|
294
|
+
total: event.total,
|
|
295
|
+
percentage: percentComplete,
|
|
296
|
+
speed: speed, // bytes/sec
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
xhr.onload = () => {
|
|
302
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
303
|
+
try {
|
|
304
|
+
const response = JSON.parse(xhr.responseText);
|
|
305
|
+
resolve(response);
|
|
306
|
+
} catch (e) {
|
|
307
|
+
reject(new Error('Invalid JSON response'));
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
try {
|
|
311
|
+
const errorResponse = JSON.parse(xhr.responseText);
|
|
312
|
+
reject(errorResponse);
|
|
313
|
+
} catch (e) {
|
|
314
|
+
reject(new Error(`Upload failed with status ${xhr.status}`));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
xhr.onerror = () => reject(new Error('Network error during upload'));
|
|
320
|
+
xhr.onabort = () => reject(new Error('Upload aborted'));
|
|
321
|
+
|
|
322
|
+
// Build URL with auth headers
|
|
323
|
+
const url = `${this.sdk.fullUrl}${endpoint}`;
|
|
324
|
+
xhr.open(method, url, true);
|
|
325
|
+
|
|
326
|
+
// IMPORTANT: Include credentials (cookies) for authentication
|
|
327
|
+
xhr.withCredentials = true;
|
|
328
|
+
|
|
329
|
+
// Add auth headers
|
|
330
|
+
if (this.sdk.token) {
|
|
331
|
+
xhr.setRequestHeader('Authorization', `Bearer ${this.sdk.token}`);
|
|
332
|
+
}
|
|
333
|
+
if (this.sdk.fwRequestId) {
|
|
334
|
+
xhr.setRequestHeader('x-request-id-fw', this.sdk.fwRequestId);
|
|
335
|
+
}
|
|
336
|
+
if (this.sdk.callId) {
|
|
337
|
+
xhr.setRequestHeader('x-call-id', this.sdk.callId);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Add environment variable override headers
|
|
341
|
+
if (process?.env?.AUTH_V3_TOKEN_TYPE_OVERRIDE) {
|
|
342
|
+
xhr.setRequestHeader(
|
|
343
|
+
'x-token-type-override',
|
|
344
|
+
process.env.AUTH_V3_TOKEN_TYPE_OVERRIDE,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
if (skipClamscan && process?.env?.CLAMSCAN_OVERRIDE_KEY) {
|
|
348
|
+
xhr.setRequestHeader(
|
|
349
|
+
'x-clamscan-override-key',
|
|
350
|
+
process.env.CLAMSCAN_OVERRIDE_KEY,
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
xhr.send(formData);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/*
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
Response:
|
|
361
|
+
{
|
|
362
|
+
"uploaded": [
|
|
363
|
+
{
|
|
364
|
+
"id": "017d0120251229hvdxjod4486582468133095",
|
|
365
|
+
"fileName": "sip-messages-20251223T195443.txt",
|
|
366
|
+
"fileSize": 18979,
|
|
367
|
+
"url": "https://masterc.api.dev-d01.app1svc.com/storage/017d0120251229hvdxjod4486582468133095.txt",
|
|
368
|
+
"mimeType": "text/plain",
|
|
369
|
+
"s3Regions": [
|
|
370
|
+
"d01",
|
|
371
|
+
"d03"
|
|
372
|
+
],
|
|
373
|
+
"isPublic": false
|
|
374
|
+
}
|
|
375
|
+
],
|
|
376
|
+
"viruses": [],
|
|
377
|
+
"errors": []
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
*/
|
|
381
|
+
/**
|
|
382
|
+
* Upload a file to storage with optional format conversion
|
|
383
|
+
* @param {Object} config - Configuration object
|
|
384
|
+
* @param {Object} config.file - File content (Buffer or File) - REQUIRED
|
|
385
|
+
* @param {string} [config.classification='generic'] - File classification (e.g., 'fax', 'files', 'generic')
|
|
386
|
+
* @param {string} [config.folder] - Folder path for organizing files
|
|
387
|
+
* @param {string} [config.fileName] - Original file name
|
|
388
|
+
* @param {boolean} [config.isPublic=false] - Whether file is publicly accessible
|
|
389
|
+
* @param {string} [config.country='US'] - Country code for region selection
|
|
390
|
+
* @param {string} [config.expireAfter] - Expiration time
|
|
391
|
+
* @param {string} [config.relatedId] - Related object ID
|
|
392
|
+
* @param {boolean} [config.createAccessKey=false] - Generate an access key for the file
|
|
393
|
+
* @param {number} [config.accessKeyExpiresIn] - Access key expiration in seconds
|
|
394
|
+
* @param {string} [config.convertTo] - Convert uploaded file to this format before storing. Supported: 'pdf', 'tiff'. Input must be PDF, DOC, or DOCX.
|
|
395
|
+
* @param {Object} [config.convertOptions] - Options for file conversion (used with convertTo)
|
|
396
|
+
* @param {('fine'|'normal')} [config.convertOptions.resolution='fine'] - Fax resolution: 'fine' (204x196) or 'normal' (204x98)
|
|
397
|
+
* @param {('letter'|'a4')} [config.convertOptions.paperSize='letter'] - Paper size for conversion
|
|
398
|
+
* @param {('g4'|'g3')} [config.convertOptions.compression='g4'] - TIFF compression: 'g4' (default) or 'g3' for older fax machines
|
|
399
|
+
* @param {Function} [config.onProgress] - Progress callback for browser uploads
|
|
400
|
+
* @param {Object} [config._options] - Internal options
|
|
401
|
+
* @returns {Promise<Object>} Upload result with uploaded files, viruses, and errors
|
|
402
|
+
*/
|
|
238
403
|
async upload({
|
|
239
404
|
classification = 'generic',
|
|
240
405
|
folder,
|
|
@@ -246,6 +411,10 @@ export class StorageService {
|
|
|
246
411
|
relatedId,
|
|
247
412
|
createAccessKey = false,
|
|
248
413
|
accessKeyExpiresIn,
|
|
414
|
+
convertTo,
|
|
415
|
+
convertOptions,
|
|
416
|
+
onProgress,
|
|
417
|
+
_options,
|
|
249
418
|
}) {
|
|
250
419
|
this.sdk.validateParams(
|
|
251
420
|
{
|
|
@@ -259,6 +428,8 @@ export class StorageService {
|
|
|
259
428
|
relatedId,
|
|
260
429
|
createAccessKey,
|
|
261
430
|
accessKeyExpiresIn,
|
|
431
|
+
convertTo,
|
|
432
|
+
convertOptions,
|
|
262
433
|
},
|
|
263
434
|
{
|
|
264
435
|
classification: { type: 'string', required: false },
|
|
@@ -270,7 +441,9 @@ export class StorageService {
|
|
|
270
441
|
expireAfter: { type: 'string', required: false },
|
|
271
442
|
relatedId: { type: 'string', required: false },
|
|
272
443
|
createAccessKey: { type: 'boolean', required: false },
|
|
273
|
-
accessKeyExpiresIn: { type: '
|
|
444
|
+
accessKeyExpiresIn: { type: 'number', required: false },
|
|
445
|
+
convertTo: { type: 'string', required: false },
|
|
446
|
+
convertOptions: { type: 'object', required: false },
|
|
274
447
|
},
|
|
275
448
|
);
|
|
276
449
|
|
|
@@ -287,12 +460,24 @@ export class StorageService {
|
|
|
287
460
|
formFields.push(['createAccessKey', createAccessKey.toString()]);
|
|
288
461
|
if (accessKeyExpiresIn)
|
|
289
462
|
formFields.push(['accessKeyExpiresIn', accessKeyExpiresIn]);
|
|
463
|
+
if (convertTo) formFields.push(['convertTo', convertTo]);
|
|
464
|
+
if (convertOptions)
|
|
465
|
+
formFields.push(['convertOptions', JSON.stringify(convertOptions)]);
|
|
290
466
|
|
|
291
|
-
return this._performUpload(
|
|
467
|
+
return this._performUpload(
|
|
468
|
+
file,
|
|
469
|
+
fileName,
|
|
470
|
+
formFields,
|
|
471
|
+
'/storage/upload',
|
|
472
|
+
'POST',
|
|
473
|
+
onProgress,
|
|
474
|
+
_options?.skipScan,
|
|
475
|
+
);
|
|
292
476
|
}
|
|
293
477
|
|
|
294
478
|
async uploadFiles(files, options = {}) {
|
|
295
|
-
const { classification, expireAfter, isPublic, metadata } =
|
|
479
|
+
const { classification, expireAfter, isPublic, metadata, _options } =
|
|
480
|
+
options;
|
|
296
481
|
|
|
297
482
|
// Validate files parameter
|
|
298
483
|
if (!files) {
|
|
@@ -392,6 +577,10 @@ export class StorageService {
|
|
|
392
577
|
if (metadata) formData.append('metadata', JSON.stringify(metadata));
|
|
393
578
|
}
|
|
394
579
|
|
|
580
|
+
if (_options?.skipScan && process?.env?.CLAMSCAN_OVERRIDE_KEY) {
|
|
581
|
+
headers['x-clamscan-override-key'] = process.env.CLAMSCAN_OVERRIDE_KEY;
|
|
582
|
+
}
|
|
583
|
+
|
|
395
584
|
const params = {
|
|
396
585
|
body: formData,
|
|
397
586
|
headers,
|
|
@@ -467,13 +656,19 @@ export class StorageService {
|
|
|
467
656
|
return result;
|
|
468
657
|
}
|
|
469
658
|
|
|
470
|
-
async uploadProfileImage({
|
|
659
|
+
async uploadProfileImage({
|
|
660
|
+
file,
|
|
661
|
+
classification = 'user_images',
|
|
662
|
+
fileName,
|
|
663
|
+
userId,
|
|
664
|
+
}) {
|
|
471
665
|
this.sdk.validateParams(
|
|
472
|
-
{ file, classification },
|
|
666
|
+
{ file, classification, userId },
|
|
473
667
|
{
|
|
474
668
|
file: { type: 'object', required: true },
|
|
475
669
|
classification: { type: 'string', required: true },
|
|
476
670
|
fileName: { type: 'string', required: false },
|
|
671
|
+
userId: { type: 'string', required: false },
|
|
477
672
|
},
|
|
478
673
|
);
|
|
479
674
|
|
|
@@ -488,6 +683,7 @@ export class StorageService {
|
|
|
488
683
|
// Build form fields exactly like the regular upload but only include classification
|
|
489
684
|
const formFields = [];
|
|
490
685
|
formFields.push(['classification', classification]);
|
|
686
|
+
formFields.push(['userId', userId]);
|
|
491
687
|
|
|
492
688
|
// Use the correct profile image endpoint with proper FormData
|
|
493
689
|
return this._performUpload(
|
|
@@ -535,12 +731,14 @@ export class StorageService {
|
|
|
535
731
|
}
|
|
536
732
|
|
|
537
733
|
async listFiles(options = {}) {
|
|
538
|
-
const { classification, limit, offset, orderBy, orderDirection } =
|
|
734
|
+
const { classification, folder, limit, offset, orderBy, orderDirection } =
|
|
735
|
+
options;
|
|
539
736
|
|
|
540
737
|
// Validate optional parameters
|
|
541
738
|
const validationSchema = {};
|
|
542
739
|
if ('classification' in options)
|
|
543
740
|
validationSchema.classification = { type: 'string' };
|
|
741
|
+
if ('folder' in options) validationSchema.folder = { type: 'string' };
|
|
544
742
|
if ('limit' in options) validationSchema.limit = { type: 'number' };
|
|
545
743
|
if ('offset' in options) validationSchema.offset = { type: 'number' };
|
|
546
744
|
if ('orderBy' in options) validationSchema.orderBy = { type: 'string' };
|
|
@@ -724,6 +922,109 @@ export class StorageService {
|
|
|
724
922
|
return result;
|
|
725
923
|
}
|
|
726
924
|
|
|
925
|
+
/**
|
|
926
|
+
* Convert an existing stored file to a different format and save it as a new storage record.
|
|
927
|
+
* The original file remains unchanged. Supported source formats: PDF, DOC, DOCX.
|
|
928
|
+
* Supported output formats: 'pdf', 'tiff'.
|
|
929
|
+
*
|
|
930
|
+
* Fields not provided in the request (classification, folder, relatedId, isPublic)
|
|
931
|
+
* are inherited from the source file. If expireAfter is not provided, the expireAt
|
|
932
|
+
* timestamp is copied directly from the source file.
|
|
933
|
+
*
|
|
934
|
+
* @param {string} storageId - ID of the existing storage file to convert (required)
|
|
935
|
+
* @param {Object} config - Conversion options
|
|
936
|
+
* @param {string} config.convertTo - Target format: 'pdf' or 'tiff' (required)
|
|
937
|
+
* @param {Object} [config.convertOptions] - Options controlling the conversion output
|
|
938
|
+
* @param {('fine'|'normal')} [config.convertOptions.resolution='fine'] - Fax resolution: 'fine' (204x196 DPI) or 'normal' (204x98 DPI)
|
|
939
|
+
* @param {('letter'|'a4')} [config.convertOptions.paperSize='letter'] - Paper size for conversion
|
|
940
|
+
* @param {('g4'|'g3')} [config.convertOptions.compression='g4'] - TIFF compression: 'g4' (modern, default) or 'g3' (legacy fax machines)
|
|
941
|
+
* @param {string} [config.classification] - Storage classification for the new file. Defaults to source file's classification.
|
|
942
|
+
* @param {string} [config.folder] - Folder path for the new file. Defaults to source file's folder.
|
|
943
|
+
* @param {string} [config.relatedId] - Related object ID for the new file. Defaults to source file's relatedId.
|
|
944
|
+
* @param {boolean} [config.isPublic] - Whether the new file is publicly accessible. Defaults to source file's isPublic.
|
|
945
|
+
* @param {string} [config.expireAfter] - Expiration duration for the new file (e.g., '7d', '1h'). If omitted, copies expireAt from source.
|
|
946
|
+
* @param {boolean} [config.createAccessKey=false] - Generate a temporary access key for the new file
|
|
947
|
+
* @param {string} [config.accessKeyExpiresIn] - Access key expiration duration (e.g., '7d')
|
|
948
|
+
* @returns {Promise<Object>} The newly created storage record for the converted file
|
|
949
|
+
* @returns {string} result.id - Storage ID of the converted file
|
|
950
|
+
* @returns {string} result.sourceStorageId - Storage ID of the original source file
|
|
951
|
+
* @returns {string} result.fileName - File name of the converted file
|
|
952
|
+
* @returns {number} result.fileSize - Size in bytes of the converted file
|
|
953
|
+
* @returns {string} result.url - URL to access the converted file
|
|
954
|
+
* @returns {string} result.mimeType - MIME type of the converted file
|
|
955
|
+
* @returns {string[]} result.s3Regions - Regions where the converted file is stored
|
|
956
|
+
* @returns {boolean} result.isPublic - Whether the converted file is public
|
|
957
|
+
* @returns {string} [result.expireAt] - Expiration timestamp if set
|
|
958
|
+
* @returns {string} [result.accessKey] - Access key if createAccessKey was true
|
|
959
|
+
*
|
|
960
|
+
* @example
|
|
961
|
+
* // Convert a stored PDF to TIFF for fax transmission
|
|
962
|
+
* const result = await sdk.storage.convertFile('017d01...', {
|
|
963
|
+
* convertTo: 'tiff',
|
|
964
|
+
* convertOptions: {
|
|
965
|
+
* resolution: 'fine',
|
|
966
|
+
* paperSize: 'letter',
|
|
967
|
+
* compression: 'g4',
|
|
968
|
+
* },
|
|
969
|
+
* });
|
|
970
|
+
* console.log(result.id); // new storage ID for the TIFF
|
|
971
|
+
* console.log(result.sourceStorageId); // original PDF storage ID
|
|
972
|
+
*
|
|
973
|
+
* @example
|
|
974
|
+
* // Convert a DOCX to PDF, overriding the classification and folder
|
|
975
|
+
* const result = await sdk.storage.convertFile('017d02...', {
|
|
976
|
+
* convertTo: 'pdf',
|
|
977
|
+
* classification: 'fax',
|
|
978
|
+
* folder: 'outbound/2026',
|
|
979
|
+
* });
|
|
980
|
+
*/
|
|
981
|
+
async convertFile(
|
|
982
|
+
storageId,
|
|
983
|
+
{
|
|
984
|
+
convertTo,
|
|
985
|
+
convertOptions,
|
|
986
|
+
classification,
|
|
987
|
+
folder,
|
|
988
|
+
relatedId,
|
|
989
|
+
isPublic,
|
|
990
|
+
expireAfter,
|
|
991
|
+
createAccessKey,
|
|
992
|
+
accessKeyExpiresIn,
|
|
993
|
+
} = {},
|
|
994
|
+
) {
|
|
995
|
+
this.sdk.validateParams(
|
|
996
|
+
{ storageId, convertTo },
|
|
997
|
+
{
|
|
998
|
+
storageId: { type: 'string', required: true },
|
|
999
|
+
convertTo: { type: 'string', required: true },
|
|
1000
|
+
convertOptions: { type: 'object', required: false },
|
|
1001
|
+
classification: { type: 'string', required: false },
|
|
1002
|
+
folder: { type: 'string', required: false },
|
|
1003
|
+
relatedId: { type: 'string', required: false },
|
|
1004
|
+
isPublic: { type: 'boolean', required: false },
|
|
1005
|
+
expireAfter: { type: 'string', required: false },
|
|
1006
|
+
createAccessKey: { type: 'boolean', required: false },
|
|
1007
|
+
accessKeyExpiresIn: { type: 'string', required: false },
|
|
1008
|
+
},
|
|
1009
|
+
);
|
|
1010
|
+
|
|
1011
|
+
const body = { convertTo };
|
|
1012
|
+
if (convertOptions !== undefined)
|
|
1013
|
+
body.convertOptions = convertOptions;
|
|
1014
|
+
if (classification !== undefined) body.classification = classification;
|
|
1015
|
+
if (folder !== undefined) body.folder = folder;
|
|
1016
|
+
if (relatedId !== undefined) body.relatedId = relatedId;
|
|
1017
|
+
if (isPublic !== undefined) body.isPublic = isPublic;
|
|
1018
|
+
if (expireAfter !== undefined) body.expireAfter = expireAfter;
|
|
1019
|
+
if (createAccessKey !== undefined) body.createAccessKey = createAccessKey;
|
|
1020
|
+
if (accessKeyExpiresIn !== undefined)
|
|
1021
|
+
body.accessKeyExpiresIn = accessKeyExpiresIn;
|
|
1022
|
+
|
|
1023
|
+
const params = { body };
|
|
1024
|
+
|
|
1025
|
+
return await this.sdk._fetch(`/storage/${storageId}/convert`, 'POST', params);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
727
1028
|
/**
|
|
728
1029
|
* Update file contents and metadata
|
|
729
1030
|
* @param {string} storageId - Storage file ID (required)
|
|
@@ -749,6 +1050,7 @@ export class StorageService {
|
|
|
749
1050
|
country,
|
|
750
1051
|
expireAfter,
|
|
751
1052
|
relatedId,
|
|
1053
|
+
_options,
|
|
752
1054
|
},
|
|
753
1055
|
) {
|
|
754
1056
|
this.sdk.validateParams(
|
|
@@ -784,6 +1086,8 @@ export class StorageService {
|
|
|
784
1086
|
formFields,
|
|
785
1087
|
`/storage/${storageId}`,
|
|
786
1088
|
'PUT',
|
|
1089
|
+
null,
|
|
1090
|
+
_options?.skipScan,
|
|
787
1091
|
);
|
|
788
1092
|
} else {
|
|
789
1093
|
// If only updating metadata, use JSON request
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export class MetricsService {
|
|
2
|
+
constructor(sdk) {
|
|
3
|
+
this.sdk = sdk;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get current task router metrics
|
|
8
|
+
* Retrieves real-time metrics for task router queues, tasks, and workers.
|
|
9
|
+
* This provides insights into queue performance, wait times, task counts, and worker activity.
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} params - Metric parameters
|
|
12
|
+
* @param {string} [params.period] - Time period for metrics calculation. Options: '5min', '15min', '30min', '1hour', '24hour'
|
|
13
|
+
* @param {string} [params.queueId] - Specific queue ID to filter metrics. If not provided, returns metrics for all queues
|
|
14
|
+
* @param {string} [params.metricType] - Type of metrics to retrieve: 'queue', 'task', 'worker', or 'all' (default: 'all')
|
|
15
|
+
* @param {number} [params.limit=100] - Maximum number of metric records to return (default: 100)
|
|
16
|
+
* @returns {Promise<Object>} Object containing the requested metrics
|
|
17
|
+
* @returns {Object} result.metrics - The metrics data organized by type
|
|
18
|
+
* @returns {Object} [result.metrics.queue] - Queue-level metrics (if metricType is 'queue' or 'all')
|
|
19
|
+
* @returns {number} result.metrics.queue.tasksWaiting - Number of tasks currently waiting in queue
|
|
20
|
+
* @returns {number} result.metrics.queue.tasksAssigned - Number of tasks currently assigned to workers
|
|
21
|
+
* @returns {number} result.metrics.queue.tasksConnected - Number of tasks currently connected/active
|
|
22
|
+
* @returns {number} result.metrics.queue.avgWaitTime - Average wait time in seconds for tasks in this period
|
|
23
|
+
* @returns {number} result.metrics.queue.longestWaitTime - Longest current wait time in seconds
|
|
24
|
+
* @returns {Object} [result.metrics.task] - Task-level metrics (if metricType is 'task' or 'all')
|
|
25
|
+
* @returns {number} result.metrics.task.created - Number of tasks created in this period
|
|
26
|
+
* @returns {number} result.metrics.task.completed - Number of tasks completed in this period
|
|
27
|
+
* @returns {number} result.metrics.task.abandoned - Number of tasks abandoned in this period
|
|
28
|
+
* @returns {Object} [result.metrics.worker] - Worker-level metrics (if metricType is 'worker' or 'all')
|
|
29
|
+
* @returns {number} result.metrics.worker.available - Number of workers currently available
|
|
30
|
+
* @returns {number} result.metrics.worker.busy - Number of workers currently busy with tasks
|
|
31
|
+
* @returns {number} result.metrics.worker.offline - Number of workers currently offline
|
|
32
|
+
* @returns {string} result.period - The time period used for calculations
|
|
33
|
+
* @returns {string} [result.queueId] - The queue ID if filtered to a specific queue
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Get all current metrics for all queues
|
|
37
|
+
* const metrics = await sdk.taskRouter.metrics.getCurrent({
|
|
38
|
+
* period: '15min',
|
|
39
|
+
* metricType: 'all'
|
|
40
|
+
* });
|
|
41
|
+
* console.log(metrics.metrics.queue.tasksWaiting); // 5
|
|
42
|
+
* console.log(metrics.metrics.worker.available); // 12
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Get queue-specific metrics for last 5 minutes
|
|
46
|
+
* const queueMetrics = await sdk.taskRouter.metrics.getCurrent({
|
|
47
|
+
* period: '5min',
|
|
48
|
+
* queueId: 'queue456',
|
|
49
|
+
* metricType: 'queue',
|
|
50
|
+
* limit: 50
|
|
51
|
+
* });
|
|
52
|
+
* console.log(queueMetrics.metrics.queue.avgWaitTime); // 45.3
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Get worker metrics for last hour
|
|
56
|
+
* const workerMetrics = await sdk.taskRouter.metrics.getCurrent({
|
|
57
|
+
* period: '1hour',
|
|
58
|
+
* metricType: 'worker'
|
|
59
|
+
* });
|
|
60
|
+
* console.log(workerMetrics.metrics.worker.available); // 8
|
|
61
|
+
* console.log(workerMetrics.metrics.worker.busy); // 4
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Get task completion metrics for last 24 hours
|
|
65
|
+
* const taskMetrics = await sdk.taskRouter.metrics.getCurrent({
|
|
66
|
+
* period: '24hour',
|
|
67
|
+
* metricType: 'task',
|
|
68
|
+
* limit: 200
|
|
69
|
+
* });
|
|
70
|
+
* console.log(taskMetrics.metrics.task.created); // 150
|
|
71
|
+
* console.log(taskMetrics.metrics.task.completed); // 142
|
|
72
|
+
*/
|
|
73
|
+
async getCurrent(accountId, params = {}) {
|
|
74
|
+
const { period, queueId, metricType, limit = 100 } = params;
|
|
75
|
+
|
|
76
|
+
this.sdk.validateParams(
|
|
77
|
+
{ period, queueId, metricType, limit },
|
|
78
|
+
{
|
|
79
|
+
period: { type: 'string', required: false },
|
|
80
|
+
queueId: { type: 'string', required: false },
|
|
81
|
+
metricType: { type: 'string', required: false },
|
|
82
|
+
limit: { type: 'number', required: false },
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const requestParams = {
|
|
87
|
+
body: {
|
|
88
|
+
limit,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
if (period !== undefined) {
|
|
93
|
+
requestParams.body.period = period;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (queueId !== undefined) {
|
|
97
|
+
requestParams.body.queueId = queueId;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (metricType !== undefined) {
|
|
101
|
+
requestParams.body.metricType = metricType;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const result = await this.sdk._fetch(
|
|
105
|
+
'/taskRouter/metrics/current',
|
|
106
|
+
'GET',
|
|
107
|
+
requestParams,
|
|
108
|
+
);
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { WorkerService } from './WorkerService.js';
|
|
2
|
+
import { TaskService } from './TaskService.js';
|
|
3
|
+
import { MetricsService } from './MetricsService.js';
|
|
4
|
+
|
|
5
|
+
export class TaskRouterService {
|
|
6
|
+
constructor(sdk) {
|
|
7
|
+
this.sdk = sdk;
|
|
8
|
+
this.worker = new WorkerService(sdk);
|
|
9
|
+
this.task = new TaskService(sdk);
|
|
10
|
+
this.metrics = new MetricsService(sdk);
|
|
11
|
+
}
|
|
12
|
+
}
|