@webex/contact-center 3.9.0-next.1 → 3.9.0-next.10
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/dist/cc.js +104 -62
- package/dist/cc.js.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/logger-proxy.js +24 -1
- package/dist/logger-proxy.js.map +1 -1
- package/dist/metrics/MetricsManager.js +1 -1
- package/dist/metrics/MetricsManager.js.map +1 -1
- package/dist/metrics/behavioral-events.js +39 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +12 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AddressBook.js +271 -0
- package/dist/services/AddressBook.js.map +1 -0
- package/dist/services/EntryPoint.js +227 -0
- package/dist/services/EntryPoint.js.map +1 -0
- package/dist/services/Queue.js +261 -0
- package/dist/services/Queue.js.map +1 -0
- package/dist/services/config/constants.js +24 -2
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +1 -43
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +0 -5
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/GlobalTypes.js.map +1 -1
- package/dist/services/core/Utils.js +121 -2
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +0 -4
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +0 -4
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/task/TaskManager.js +1 -0
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/index.js +145 -71
- package/dist/services/task/index.js.map +1 -1
- package/dist/types/cc.d.ts +77 -43
- package/dist/types/index.d.ts +3 -2
- package/dist/types/metrics/constants.d.ts +7 -0
- package/dist/types/services/AddressBook.d.ts +74 -0
- package/dist/types/services/EntryPoint.d.ts +67 -0
- package/dist/types/services/Queue.d.ts +76 -0
- package/dist/types/services/config/constants.d.ts +23 -1
- package/dist/types/services/config/index.d.ts +1 -14
- package/dist/types/services/config/types.d.ts +0 -64
- package/dist/types/services/core/GlobalTypes.d.ts +25 -0
- package/dist/types/services/core/Utils.d.ts +27 -1
- package/dist/types/services/task/index.d.ts +1 -1
- package/dist/types/types.d.ts +162 -0
- package/dist/types/utils/PageCache.d.ts +173 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/PageCache.js +192 -0
- package/dist/utils/PageCache.js.map +1 -0
- package/dist/webex.js +1 -1
- package/package.json +8 -8
- package/src/cc.ts +120 -80
- package/src/index.ts +16 -2
- package/src/logger-proxy.ts +24 -1
- package/src/metrics/MetricsManager.ts +1 -1
- package/src/metrics/behavioral-events.ts +42 -0
- package/src/metrics/constants.ts +15 -0
- package/src/services/AddressBook.ts +291 -0
- package/src/services/EntryPoint.ts +241 -0
- package/src/services/Queue.ts +277 -0
- package/src/services/config/constants.ts +26 -2
- package/src/services/config/index.ts +1 -55
- package/src/services/config/types.ts +0 -65
- package/src/services/core/GlobalTypes.ts +27 -0
- package/src/services/core/Utils.ts +155 -1
- package/src/services/core/aqm-reqs.ts +0 -5
- package/src/services/core/websocket/WebSocketManager.ts +0 -4
- package/src/services/task/TaskManager.ts +1 -0
- package/src/services/task/index.ts +172 -56
- package/src/types.ts +180 -0
- package/src/utils/PageCache.ts +252 -0
- package/test/unit/spec/cc.ts +30 -82
- package/test/unit/spec/metrics/MetricsManager.ts +0 -1
- package/test/unit/spec/services/AddressBook.ts +332 -0
- package/test/unit/spec/services/EntryPoint.ts +259 -0
- package/test/unit/spec/services/Queue.ts +323 -0
- package/test/unit/spec/services/config/index.ts +0 -71
- package/test/unit/spec/services/core/Utils.ts +50 -0
- package/test/unit/spec/services/core/aqm-reqs.ts +1 -3
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +0 -4
- package/test/unit/spec/services/task/TaskManager.ts +8 -1
- package/test/unit/spec/services/task/index.ts +226 -122
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/src/cc.ts
CHANGED
|
@@ -44,13 +44,8 @@ import WebexRequest from './services/core/WebexRequest';
|
|
|
44
44
|
import LoggerProxy from './logger-proxy';
|
|
45
45
|
import {StateChange, Logout, StateChangeSuccess, AGENT_EVENTS} from './services/agent/types';
|
|
46
46
|
import {getErrorDetails, isValidDialNumber} from './services/core/Utils';
|
|
47
|
-
import {Profile, WelcomeEvent, CC_EVENTS
|
|
48
|
-
import {
|
|
49
|
-
AGENT_STATE_AVAILABLE,
|
|
50
|
-
AGENT_STATE_AVAILABLE_ID,
|
|
51
|
-
DEFAULT_PAGE,
|
|
52
|
-
DEFAULT_PAGE_SIZE,
|
|
53
|
-
} from './services/config/constants';
|
|
47
|
+
import {Profile, WelcomeEvent, CC_EVENTS} from './services/config/types';
|
|
48
|
+
import {AGENT_STATE_AVAILABLE, AGENT_STATE_AVAILABLE_ID} from './services/config/constants';
|
|
54
49
|
import {ConnectionLostDetails} from './services/core/websocket/types';
|
|
55
50
|
import TaskManager from './services/task/TaskManager';
|
|
56
51
|
import WebCallingService from './services/WebCallingService';
|
|
@@ -58,6 +53,15 @@ import {ITask, TASK_EVENTS, TaskResponse, DialerPayload} from './services/task/t
|
|
|
58
53
|
import MetricsManager from './metrics/MetricsManager';
|
|
59
54
|
import {METRIC_EVENT_NAMES} from './metrics/constants';
|
|
60
55
|
import {Failure} from './services/core/GlobalTypes';
|
|
56
|
+
import EntryPoint from './services/EntryPoint';
|
|
57
|
+
import AddressBook from './services/AddressBook';
|
|
58
|
+
import Queue from './services/Queue';
|
|
59
|
+
import type {
|
|
60
|
+
EntryPointListResponse,
|
|
61
|
+
EntryPointSearchParams,
|
|
62
|
+
ContactServiceQueuesResponse,
|
|
63
|
+
ContactServiceQueueSearchParams,
|
|
64
|
+
} from './types';
|
|
61
65
|
|
|
62
66
|
/**
|
|
63
67
|
* The main Contact Center plugin class that enables integration with Webex Contact Center.
|
|
@@ -163,6 +167,7 @@ import {Failure} from './services/core/GlobalTypes';
|
|
|
163
167
|
*
|
|
164
168
|
* @public
|
|
165
169
|
*/
|
|
170
|
+
|
|
166
171
|
export default class ContactCenter extends WebexPlugin implements IContactCenter {
|
|
167
172
|
/**
|
|
168
173
|
* The plugin's unique namespace identifier in the Webex SDK.
|
|
@@ -243,6 +248,71 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
243
248
|
*/
|
|
244
249
|
private metricsManager: MetricsManager;
|
|
245
250
|
|
|
251
|
+
/**
|
|
252
|
+
* API instance for managing Webex Contact Center entry points
|
|
253
|
+
* Provides functionality to fetch entry points with caching support
|
|
254
|
+
* @type {EntryPoint}
|
|
255
|
+
* @public
|
|
256
|
+
* @example
|
|
257
|
+
* ```typescript
|
|
258
|
+
* const cc = webex.cc;
|
|
259
|
+
* await cc.register();
|
|
260
|
+
* await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
|
|
261
|
+
*
|
|
262
|
+
* // Access EntryPointRecord
|
|
263
|
+
* const response = await cc.entryPoint.getEntryPoints({
|
|
264
|
+
* page: 0,
|
|
265
|
+
* pageSize: 50
|
|
266
|
+
* });
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
private entryPoint: EntryPoint;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* API instance for managing Webex Contact Center address book contacts
|
|
273
|
+
* Provides functionality to fetch address book entries with caching support
|
|
274
|
+
* @type {AddressBook}
|
|
275
|
+
* @public
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* const cc = webex.cc;
|
|
279
|
+
* await cc.register();
|
|
280
|
+
* await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
|
|
281
|
+
*
|
|
282
|
+
* // Access AddressBook API
|
|
283
|
+
* const response = await cc.addressBook.getEntries({
|
|
284
|
+
* page: 0,
|
|
285
|
+
* pageSize: 25
|
|
286
|
+
* });
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
public addressBook: AddressBook;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* API instance for managing Webex Contact Center queues
|
|
293
|
+
* Provides functionality to fetch queues with caching support
|
|
294
|
+
* @type {Queue}
|
|
295
|
+
* @public
|
|
296
|
+
* @example
|
|
297
|
+
* ```typescript
|
|
298
|
+
* const cc = webex.cc;
|
|
299
|
+
* await cc.register();
|
|
300
|
+
* await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
|
|
301
|
+
*
|
|
302
|
+
* // Access Queue API
|
|
303
|
+
* const response = await cc.queue.getQueues({
|
|
304
|
+
* page: 0,
|
|
305
|
+
* pageSize: 50
|
|
306
|
+
* });
|
|
307
|
+
*
|
|
308
|
+
* // Filter queues by specific criteria
|
|
309
|
+
* const filteredQueues = await cc.queue.getQueues({
|
|
310
|
+
* filter: 'id=="queue-id-123"'
|
|
311
|
+
* });
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
private queue: Queue;
|
|
315
|
+
|
|
246
316
|
/**
|
|
247
317
|
* Logger utility for Contact Center plugin
|
|
248
318
|
* Provides consistent logging across the plugin
|
|
@@ -289,6 +359,13 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
289
359
|
);
|
|
290
360
|
this.incomingTaskListener();
|
|
291
361
|
|
|
362
|
+
// Initialize API instances
|
|
363
|
+
// will have future function for indivdual fetch etc so better be in an object
|
|
364
|
+
this.entryPoint = new EntryPoint(this.$webex);
|
|
365
|
+
this.addressBook = new AddressBook(this.$webex, () => this.agentConfig?.addressBookId);
|
|
366
|
+
this.queue = new Queue(this.$webex);
|
|
367
|
+
|
|
368
|
+
// Initialize logger
|
|
292
369
|
LoggerProxy.initialize(this.$webex.logger);
|
|
293
370
|
});
|
|
294
371
|
}
|
|
@@ -984,6 +1061,20 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
984
1061
|
method: METHODS.HANDLE_WEBSOCKET_MESSAGE,
|
|
985
1062
|
});
|
|
986
1063
|
|
|
1064
|
+
// Emit metrics for all websocket events except keepalive and welcome
|
|
1065
|
+
const topLevelType = eventData.type;
|
|
1066
|
+
const nestedType = eventData?.data?.type;
|
|
1067
|
+
if (topLevelType !== CC_EVENTS.WELCOME && eventData.keepalive !== 'true') {
|
|
1068
|
+
const metricsPayload: Record<string, any> = {
|
|
1069
|
+
ws_event_type: nestedType || topLevelType,
|
|
1070
|
+
top_level_type: topLevelType,
|
|
1071
|
+
has_data: Boolean(eventData.data),
|
|
1072
|
+
};
|
|
1073
|
+
this.metricsManager.trackEvent(METRIC_EVENT_NAMES.WEBSOCKET_EVENT_RECEIVED, metricsPayload, [
|
|
1074
|
+
'operational',
|
|
1075
|
+
]);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
987
1078
|
switch (eventData.type) {
|
|
988
1079
|
case CC_EVENTS.AGENT_MULTI_LOGIN:
|
|
989
1080
|
// @ts-ignore
|
|
@@ -1398,79 +1489,6 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1398
1489
|
}
|
|
1399
1490
|
}
|
|
1400
1491
|
|
|
1401
|
-
/**
|
|
1402
|
-
* This is used for getting the list of queues to which a task can be consulted or transferred.
|
|
1403
|
-
* @param {string} [search] - Optional search string to filter queues by name
|
|
1404
|
-
* @param {string} [filter] - Optional OData filter expression (e.g., 'teamId eq "team123"')
|
|
1405
|
-
* @param {number} [page=0] - Page number for paginated results, starting at 0
|
|
1406
|
-
* @param {number} [pageSize=100] - Number of queues to return per page
|
|
1407
|
-
* @returns Promise<ContactServiceQueue[]> Resolves with the list of queues
|
|
1408
|
-
* @throws Error If the operation fails
|
|
1409
|
-
* @public
|
|
1410
|
-
* @example
|
|
1411
|
-
* ```typescript
|
|
1412
|
-
* const cc = webex.cc;
|
|
1413
|
-
* await cc.register();
|
|
1414
|
-
* await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
|
|
1415
|
-
*
|
|
1416
|
-
* // Basic usage - get all queues
|
|
1417
|
-
* const allQueues = await cc.getQueues();
|
|
1418
|
-
*
|
|
1419
|
-
* // Search for specific queues
|
|
1420
|
-
* const salesQueues = await cc.getQueues('sales'); // Search for 'sales' in queue names
|
|
1421
|
-
*
|
|
1422
|
-
* // Use filtering and pagination
|
|
1423
|
-
* const filteredQueues = await cc.getQueues(
|
|
1424
|
-
* '', // No search term
|
|
1425
|
-
* 'teamId eq "team123"', // Filter by team
|
|
1426
|
-
* 0, // First page
|
|
1427
|
-
* 50 // 50 items per page
|
|
1428
|
-
* );
|
|
1429
|
-
*
|
|
1430
|
-
* // Process queue results
|
|
1431
|
-
* queues.forEach(queue => {
|
|
1432
|
-
* console.log('Queue:', {
|
|
1433
|
-
* id: queue.queueId,
|
|
1434
|
-
* name: queue.queueName,
|
|
1435
|
-
* channelType: queue.channelType,
|
|
1436
|
-
* isActive: queue.isActive,
|
|
1437
|
-
* description: queue.description
|
|
1438
|
-
* });
|
|
1439
|
-
* });
|
|
1440
|
-
* ```
|
|
1441
|
-
*/
|
|
1442
|
-
public async getQueues(
|
|
1443
|
-
search?: string,
|
|
1444
|
-
filter?: string,
|
|
1445
|
-
page = DEFAULT_PAGE,
|
|
1446
|
-
pageSize = DEFAULT_PAGE_SIZE
|
|
1447
|
-
): Promise<ContactServiceQueue[]> {
|
|
1448
|
-
LoggerProxy.info('Fetching queues', {
|
|
1449
|
-
module: CC_FILE,
|
|
1450
|
-
method: METHODS.GET_QUEUES,
|
|
1451
|
-
});
|
|
1452
|
-
|
|
1453
|
-
const orgId = this.$webex.credentials.getOrgId();
|
|
1454
|
-
|
|
1455
|
-
if (!orgId) {
|
|
1456
|
-
LoggerProxy.error('Org ID not found.', {
|
|
1457
|
-
module: CC_FILE,
|
|
1458
|
-
method: METHODS.GET_QUEUES,
|
|
1459
|
-
});
|
|
1460
|
-
|
|
1461
|
-
throw new Error('Org ID not found.');
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
const result = await this.services.config.getQueues(orgId, page, pageSize, search, filter);
|
|
1465
|
-
|
|
1466
|
-
LoggerProxy.log(`Successfully retrieved ${result?.length} queues`, {
|
|
1467
|
-
module: CC_FILE,
|
|
1468
|
-
method: METHODS.GET_QUEUES,
|
|
1469
|
-
});
|
|
1470
|
-
|
|
1471
|
-
return result;
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
1492
|
/**
|
|
1475
1493
|
* Uploads logs to help troubleshoot SDK issues.
|
|
1476
1494
|
*
|
|
@@ -1615,4 +1633,26 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1615
1633
|
throw error;
|
|
1616
1634
|
}
|
|
1617
1635
|
}
|
|
1636
|
+
|
|
1637
|
+
/**
|
|
1638
|
+
* Returns paginated entry points for the organization.
|
|
1639
|
+
* Thin wrapper around internal EntryPoint instance.
|
|
1640
|
+
* @public
|
|
1641
|
+
*/
|
|
1642
|
+
public async getEntryPoints(
|
|
1643
|
+
params: EntryPointSearchParams = {}
|
|
1644
|
+
): Promise<EntryPointListResponse> {
|
|
1645
|
+
return this.entryPoint.getEntryPoints(params);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Returns paginated contact service queues for the organization.
|
|
1650
|
+
* Thin wrapper around internal Queue instance.
|
|
1651
|
+
* @public
|
|
1652
|
+
*/
|
|
1653
|
+
public async getQueues(
|
|
1654
|
+
params: ContactServiceQueueSearchParams = {}
|
|
1655
|
+
): Promise<ContactServiceQueuesResponse> {
|
|
1656
|
+
return this.queue.getQueues(params);
|
|
1657
|
+
}
|
|
1618
1658
|
}
|
package/src/index.ts
CHANGED
|
@@ -26,6 +26,22 @@ export {default as Task} from './services/task';
|
|
|
26
26
|
*/
|
|
27
27
|
export {default as routingAgent} from './services/agent';
|
|
28
28
|
|
|
29
|
+
// API exports (AddressBook is public, EntryPoint and Queue are accessed via cc wrappers)
|
|
30
|
+
export {default as AddressBook} from './services/AddressBook';
|
|
31
|
+
|
|
32
|
+
/** EntryPoint API types */
|
|
33
|
+
export type {
|
|
34
|
+
EntryPointRecord,
|
|
35
|
+
EntryPointListResponse,
|
|
36
|
+
EntryPointSearchParams,
|
|
37
|
+
AddressBookEntry,
|
|
38
|
+
AddressBookEntriesResponse,
|
|
39
|
+
AddressBookEntrySearchParams,
|
|
40
|
+
ContactServiceQueuesResponse,
|
|
41
|
+
ContactServiceQueueSearchParams,
|
|
42
|
+
ContactServiceQueue,
|
|
43
|
+
} from './types';
|
|
44
|
+
|
|
29
45
|
// Enums
|
|
30
46
|
/**
|
|
31
47
|
* Task Events for Contact Center operations
|
|
@@ -157,8 +173,6 @@ export type {
|
|
|
157
173
|
export type {
|
|
158
174
|
/** Profile interface */
|
|
159
175
|
Profile,
|
|
160
|
-
/** Contact service queue interface */
|
|
161
|
-
ContactServiceQueue,
|
|
162
176
|
/** Response type from getUserUsingCI method */
|
|
163
177
|
AgentResponse,
|
|
164
178
|
/** Response from getDesktopProfileById */
|
package/src/logger-proxy.ts
CHANGED
|
@@ -105,6 +105,29 @@ export default class LoggerProxy {
|
|
|
105
105
|
const interactionId = context.interactionId ? ` - interactionId:${context.interactionId}` : '';
|
|
106
106
|
const trackingId = context.trackingId ? ` - trackingId:${context.trackingId}` : '';
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
// Format additional data if provided
|
|
109
|
+
let dataString = '';
|
|
110
|
+
if (context.data) {
|
|
111
|
+
try {
|
|
112
|
+
dataString = ` - data:${JSON.stringify(context.data)}`;
|
|
113
|
+
} catch (e) {
|
|
114
|
+
dataString = ` - data:[object]`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Format error if provided
|
|
119
|
+
let errorString = '';
|
|
120
|
+
if (context.error) {
|
|
121
|
+
if (context.error instanceof Error) {
|
|
122
|
+
errorString = ` - error:${context.error.name}:${context.error.message}`;
|
|
123
|
+
if (context.error.stack && level === LOGGING_LEVEL.error) {
|
|
124
|
+
errorString += ` - stack:${context.error.stack}`;
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
errorString = ` - error:${String(context.error)}`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return `${timestamp} ${LOG_PREFIX} - [${level}]: module:${moduleName} - method:${methodName}${interactionId}${trackingId}${dataString}${errorString} - ${message}`;
|
|
109
132
|
}
|
|
110
133
|
}
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
} from '@webex/internal-plugin-metrics/src/metrics.types';
|
|
7
7
|
|
|
8
8
|
import {WebexSDK} from '../types';
|
|
9
|
-
import {BehavioralEventTaxonomy, getEventTaxonomy} from './behavioral-events';
|
|
10
9
|
import LoggerProxy from '../logger-proxy';
|
|
10
|
+
import {BehavioralEventTaxonomy, getEventTaxonomy} from './behavioral-events';
|
|
11
11
|
import {METRIC_EVENT_NAMES} from './constants';
|
|
12
12
|
import {Failure} from '../services/core/GlobalTypes';
|
|
13
13
|
import {PRODUCT_NAME} from '../constants';
|
|
@@ -329,6 +329,48 @@ const eventTaxonomyMap: Record<string, BehavioralEventTaxonomy> = {
|
|
|
329
329
|
target: 'agent_device_type_update',
|
|
330
330
|
verb: 'fail',
|
|
331
331
|
},
|
|
332
|
+
|
|
333
|
+
// EntryPoint API Events
|
|
334
|
+
[METRIC_EVENT_NAMES.ENTRYPOINT_FETCH_SUCCESS]: {
|
|
335
|
+
product,
|
|
336
|
+
agent: 'user',
|
|
337
|
+
target: 'entrypoint_fetch',
|
|
338
|
+
verb: 'complete',
|
|
339
|
+
},
|
|
340
|
+
[METRIC_EVENT_NAMES.ENTRYPOINT_FETCH_FAILED]: {
|
|
341
|
+
product,
|
|
342
|
+
agent: 'user',
|
|
343
|
+
target: 'entrypoint_fetch',
|
|
344
|
+
verb: 'fail',
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
// AddressBook API Events
|
|
348
|
+
[METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_SUCCESS]: {
|
|
349
|
+
product,
|
|
350
|
+
agent: 'user',
|
|
351
|
+
target: 'addressbook_fetch',
|
|
352
|
+
verb: 'complete',
|
|
353
|
+
},
|
|
354
|
+
[METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_FAILED]: {
|
|
355
|
+
product,
|
|
356
|
+
agent: 'user',
|
|
357
|
+
target: 'addressbook_fetch',
|
|
358
|
+
verb: 'fail',
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
// Queue API Events
|
|
362
|
+
[METRIC_EVENT_NAMES.QUEUE_FETCH_SUCCESS]: {
|
|
363
|
+
product,
|
|
364
|
+
agent: 'user',
|
|
365
|
+
target: 'queue_fetch',
|
|
366
|
+
verb: 'complete',
|
|
367
|
+
},
|
|
368
|
+
[METRIC_EVENT_NAMES.QUEUE_FETCH_FAILED]: {
|
|
369
|
+
product,
|
|
370
|
+
agent: 'user',
|
|
371
|
+
target: 'queue_fetch',
|
|
372
|
+
verb: 'fail',
|
|
373
|
+
},
|
|
332
374
|
};
|
|
333
375
|
|
|
334
376
|
/**
|
package/src/metrics/constants.ts
CHANGED
|
@@ -117,8 +117,23 @@ export const METRIC_EVENT_NAMES = {
|
|
|
117
117
|
WEBSOCKET_DEREGISTER_SUCCESS: 'Websocket Deregister Success',
|
|
118
118
|
WEBSOCKET_DEREGISTER_FAIL: 'Websocket Deregister Failed',
|
|
119
119
|
|
|
120
|
+
// WebSocket message events
|
|
121
|
+
WEBSOCKET_EVENT_RECEIVED: 'Websocket Event Received',
|
|
122
|
+
|
|
120
123
|
AGENT_DEVICE_TYPE_UPDATE_SUCCESS: 'Agent Device Type Update Success',
|
|
121
124
|
AGENT_DEVICE_TYPE_UPDATE_FAILED: 'Agent Device Type Update Failed',
|
|
125
|
+
|
|
126
|
+
// EntryPoint API Events
|
|
127
|
+
ENTRYPOINT_FETCH_SUCCESS: 'Entrypoint Fetch Success',
|
|
128
|
+
ENTRYPOINT_FETCH_FAILED: 'Entrypoint Fetch Failed',
|
|
129
|
+
|
|
130
|
+
// AddressBook API Events
|
|
131
|
+
ADDRESSBOOK_FETCH_SUCCESS: 'AddressBook Fetch Success',
|
|
132
|
+
ADDRESSBOOK_FETCH_FAILED: 'AddressBook Fetch Failed',
|
|
133
|
+
|
|
134
|
+
// Queue API Events
|
|
135
|
+
QUEUE_FETCH_SUCCESS: 'Queue Fetch Success',
|
|
136
|
+
QUEUE_FETCH_FAILED: 'Queue Fetch Failed',
|
|
122
137
|
} as const;
|
|
123
138
|
|
|
124
139
|
/**
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import {HTTP_METHODS, WebexSDK} from '../types';
|
|
2
|
+
import type {
|
|
3
|
+
AddressBookEntry,
|
|
4
|
+
AddressBookEntriesResponse,
|
|
5
|
+
AddressBookEntrySearchParams,
|
|
6
|
+
} from '../types';
|
|
7
|
+
import LoggerProxy from '../logger-proxy';
|
|
8
|
+
import WebexRequest from './core/WebexRequest';
|
|
9
|
+
import PageCache, {PAGINATION_DEFAULTS} from '../utils/PageCache';
|
|
10
|
+
import MetricsManager from '../metrics/MetricsManager';
|
|
11
|
+
import {WCC_API_GATEWAY} from './constants';
|
|
12
|
+
import {endPointMap} from './config/constants';
|
|
13
|
+
import {METRIC_EVENT_NAMES} from '../metrics/constants';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* AddressBook API class for managing Webex Contact Center address book entries.
|
|
17
|
+
* Provides functionality to fetch address book entries using the entry API.
|
|
18
|
+
*
|
|
19
|
+
* @class AddressBook
|
|
20
|
+
* @public
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import Webex from 'webex';
|
|
24
|
+
*
|
|
25
|
+
* const webex = new Webex({ credentials: 'YOUR_ACCESS_TOKEN' });
|
|
26
|
+
* const cc = webex.cc;
|
|
27
|
+
*
|
|
28
|
+
* // Register and login first
|
|
29
|
+
* await cc.register();
|
|
30
|
+
* await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
|
|
31
|
+
*
|
|
32
|
+
* // Get AddressBook API instance from ContactCenter
|
|
33
|
+
* const addressBookAPI = cc.addressBook;
|
|
34
|
+
*
|
|
35
|
+
* // Get entries from agent's default address book
|
|
36
|
+
* const entries = await addressBookAPI.getEntries();
|
|
37
|
+
*
|
|
38
|
+
* // Get entries from a specific address book with pagination
|
|
39
|
+
* const entries = await addressBookAPI.getEntries({
|
|
40
|
+
* addressBookId: 'addressBookId123',
|
|
41
|
+
* page: 0,
|
|
42
|
+
* pageSize: 50
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* // Search for specific entries
|
|
46
|
+
* const searchResults = await addressBook.getEntries({
|
|
47
|
+
* search: 'john',
|
|
48
|
+
* filter: 'name=="John Doe"'
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export class AddressBook {
|
|
53
|
+
private webexRequest: WebexRequest;
|
|
54
|
+
private webex: WebexSDK;
|
|
55
|
+
private getAddressBookId: () => string;
|
|
56
|
+
private metricsManager: MetricsManager;
|
|
57
|
+
|
|
58
|
+
// Page cache using the common utility
|
|
59
|
+
private pageCache: PageCache<AddressBookEntry>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Creates an instance of AddressBook
|
|
63
|
+
* @param {WebexSDK} webex - The Webex SDK instance
|
|
64
|
+
* @param {() => string} getAddressBookId - Function to get the addressBookId from agent profile
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
constructor(webex: WebexSDK, getAddressBookId: () => string) {
|
|
68
|
+
this.webex = webex;
|
|
69
|
+
this.webexRequest = WebexRequest.getInstance({webex});
|
|
70
|
+
this.getAddressBookId = getAddressBookId;
|
|
71
|
+
this.pageCache = new PageCache<AddressBookEntry>('AddressBook');
|
|
72
|
+
this.metricsManager = MetricsManager.getInstance({webex});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Fetches address book entries for a specific address book using the entry API
|
|
77
|
+
* @param {AddressBookEntrySearchParams} [params] - Search and pagination parameters including addressBookId
|
|
78
|
+
* @returns {Promise<AddressBookEntriesResponse>} Promise resolving to address book entries
|
|
79
|
+
* @throws {Error} If the API call fails
|
|
80
|
+
* @public
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* // Get entries from agent's default address book
|
|
84
|
+
* const response = await addressBookAPI.getEntries();
|
|
85
|
+
*
|
|
86
|
+
* // Get entries from a specific address book with pagination
|
|
87
|
+
* const response = await addressBookAPI.getEntries({
|
|
88
|
+
* addressBookId: 'addressBookId123',
|
|
89
|
+
* page: 0,
|
|
90
|
+
* pageSize: 25
|
|
91
|
+
* });
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
public async getEntries(
|
|
95
|
+
params: AddressBookEntrySearchParams = {}
|
|
96
|
+
): Promise<AddressBookEntriesResponse> {
|
|
97
|
+
const startTime = Date.now();
|
|
98
|
+
const {
|
|
99
|
+
addressBookId,
|
|
100
|
+
page = PAGINATION_DEFAULTS.PAGE,
|
|
101
|
+
pageSize = PAGINATION_DEFAULTS.PAGE_SIZE,
|
|
102
|
+
search,
|
|
103
|
+
filter,
|
|
104
|
+
attributes,
|
|
105
|
+
} = params;
|
|
106
|
+
|
|
107
|
+
// Use provided addressBookId or fall back to agent's address book
|
|
108
|
+
const bookId = addressBookId || this.getAddressBookId();
|
|
109
|
+
const orgId = this.webex.credentials.getOrgId();
|
|
110
|
+
const isSearchRequest = !!(search || filter || attributes);
|
|
111
|
+
|
|
112
|
+
LoggerProxy.info('Fetching address book entries', {
|
|
113
|
+
module: 'AddressBook',
|
|
114
|
+
method: 'getEntries',
|
|
115
|
+
data: {
|
|
116
|
+
orgId,
|
|
117
|
+
bookId,
|
|
118
|
+
page,
|
|
119
|
+
pageSize,
|
|
120
|
+
isSearchRequest,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Check if we can use cache for simple pagination (no search/filter/attributes)
|
|
125
|
+
if (this.pageCache.canUseCache({search, filter, attributes})) {
|
|
126
|
+
const cacheKey = this.pageCache.buildCacheKey(bookId, page, pageSize);
|
|
127
|
+
const cachedPage = this.pageCache.getCachedPage(cacheKey);
|
|
128
|
+
|
|
129
|
+
if (cachedPage) {
|
|
130
|
+
const duration = Date.now() - startTime;
|
|
131
|
+
|
|
132
|
+
LoggerProxy.info(`Returning page ${page} from cache`, {
|
|
133
|
+
module: 'AddressBook',
|
|
134
|
+
method: 'getEntries',
|
|
135
|
+
data: {
|
|
136
|
+
cacheHit: true,
|
|
137
|
+
duration,
|
|
138
|
+
recordCount: cachedPage.data.length,
|
|
139
|
+
page,
|
|
140
|
+
pageSize,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
data: cachedPage.data,
|
|
146
|
+
meta: {
|
|
147
|
+
page,
|
|
148
|
+
pageSize,
|
|
149
|
+
totalPages: cachedPage.totalMeta?.totalPages,
|
|
150
|
+
totalRecords: cachedPage.totalMeta?.totalRecords,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Start timing for the operation
|
|
157
|
+
this.metricsManager.timeEvent(METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_SUCCESS);
|
|
158
|
+
|
|
159
|
+
// Validate address book id early to avoid bad requests
|
|
160
|
+
if (!bookId) {
|
|
161
|
+
const errorData = {
|
|
162
|
+
orgId,
|
|
163
|
+
bookId,
|
|
164
|
+
isSearchRequest,
|
|
165
|
+
page,
|
|
166
|
+
pageSize,
|
|
167
|
+
error: 'Missing addressBookId for agent. Ensure agent profile contains addressBookId.',
|
|
168
|
+
};
|
|
169
|
+
LoggerProxy.error('AddressBook called without a valid addressBookId', {
|
|
170
|
+
module: 'AddressBook',
|
|
171
|
+
method: 'getEntries',
|
|
172
|
+
data: errorData,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
this.metricsManager.trackEvent(METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_FAILED, errorData, [
|
|
176
|
+
'behavioral',
|
|
177
|
+
'operational',
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
throw new Error('AddressBook: addressBookId is not available for the current agent.');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
// Build query parameters according to spec
|
|
185
|
+
const queryParams = new URLSearchParams({
|
|
186
|
+
page: page.toString(),
|
|
187
|
+
pageSize: pageSize.toString(),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (filter) queryParams.append('filter', filter);
|
|
191
|
+
if (attributes) queryParams.append('attributes', attributes);
|
|
192
|
+
if (search) queryParams.append('search', search);
|
|
193
|
+
|
|
194
|
+
const resource = endPointMap.addressBookEntries(orgId, bookId, queryParams.toString());
|
|
195
|
+
|
|
196
|
+
LoggerProxy.info('Making API request to fetch address book entries', {
|
|
197
|
+
module: 'AddressBook',
|
|
198
|
+
method: 'getEntries',
|
|
199
|
+
data: {
|
|
200
|
+
resource,
|
|
201
|
+
service: WCC_API_GATEWAY,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const response = await this.webexRequest.request({
|
|
206
|
+
service: WCC_API_GATEWAY,
|
|
207
|
+
resource,
|
|
208
|
+
method: HTTP_METHODS.GET,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const duration = Date.now() - startTime;
|
|
212
|
+
|
|
213
|
+
const recordCount = response.body?.data?.length || 0;
|
|
214
|
+
const totalRecords = response.body?.meta?.totalRecords;
|
|
215
|
+
|
|
216
|
+
LoggerProxy.info(`Successfully retrieved ${recordCount} address book entries`, {
|
|
217
|
+
module: 'AddressBook',
|
|
218
|
+
method: 'getEntries',
|
|
219
|
+
data: {
|
|
220
|
+
statusCode: response.statusCode,
|
|
221
|
+
duration,
|
|
222
|
+
recordCount,
|
|
223
|
+
totalRecords,
|
|
224
|
+
isSearchRequest,
|
|
225
|
+
page,
|
|
226
|
+
pageSize,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Only track metrics for search requests or first page loads to reduce metric volume
|
|
231
|
+
if (isSearchRequest || page === 0) {
|
|
232
|
+
this.metricsManager.trackEvent(
|
|
233
|
+
METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_SUCCESS,
|
|
234
|
+
{
|
|
235
|
+
orgId,
|
|
236
|
+
bookId,
|
|
237
|
+
statusCode: response.statusCode,
|
|
238
|
+
recordCount,
|
|
239
|
+
totalRecords,
|
|
240
|
+
isSearchRequest,
|
|
241
|
+
isFirstPage: page === 0,
|
|
242
|
+
},
|
|
243
|
+
['behavioral', 'operational']
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Cache the page data for simple pagination (no search/filter/attributes)
|
|
248
|
+
if (this.pageCache.canUseCache({search, filter, attributes}) && response.body?.data) {
|
|
249
|
+
const cacheKey = this.pageCache.buildCacheKey(bookId, page, pageSize);
|
|
250
|
+
this.pageCache.cachePage(cacheKey, response.body.data, response.body.meta);
|
|
251
|
+
|
|
252
|
+
LoggerProxy.info('Cached address book entries for future requests', {
|
|
253
|
+
module: 'AddressBook',
|
|
254
|
+
method: 'getEntries',
|
|
255
|
+
data: {
|
|
256
|
+
cacheKey,
|
|
257
|
+
recordCount,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return response.body;
|
|
263
|
+
} catch (error) {
|
|
264
|
+
const errorData = {
|
|
265
|
+
orgId,
|
|
266
|
+
bookId,
|
|
267
|
+
error: error instanceof Error ? error.message : String(error),
|
|
268
|
+
isSearchRequest,
|
|
269
|
+
page,
|
|
270
|
+
pageSize,
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
LoggerProxy.error('Failed to fetch address book entries', {
|
|
274
|
+
module: 'AddressBook',
|
|
275
|
+
method: 'getEntries',
|
|
276
|
+
data: errorData,
|
|
277
|
+
error,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Track all failures for troubleshooting
|
|
281
|
+
this.metricsManager.trackEvent(METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_FAILED, errorData, [
|
|
282
|
+
'behavioral',
|
|
283
|
+
'operational',
|
|
284
|
+
]);
|
|
285
|
+
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export default AddressBook;
|