@webex/contact-center 3.8.1 → 3.9.0-multiple-llm.1
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 +196 -47
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/index.js +13 -1
- 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 +88 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +32 -2
- 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 +36 -2
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +29 -21
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +33 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/GlobalTypes.js.map +1 -1
- package/dist/services/core/Utils.js +162 -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 +74 -2
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/constants.js +7 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/contact.js +86 -0
- package/dist/services/task/contact.js.map +1 -1
- package/dist/services/task/index.js +384 -72
- package/dist/services/task/index.js.map +1 -1
- package/dist/services/task/types.js +14 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +115 -35
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/index.d.ts +8 -3
- package/dist/types/metrics/constants.d.ts +25 -1
- 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 +35 -1
- package/dist/types/services/config/index.d.ts +6 -9
- package/dist/types/services/config/types.d.ts +79 -58
- package/dist/types/services/core/GlobalTypes.d.ts +25 -0
- package/dist/types/services/core/Utils.d.ts +40 -1
- package/dist/types/services/task/constants.d.ts +6 -0
- package/dist/types/services/task/contact.d.ts +10 -0
- package/dist/types/services/task/index.d.ts +44 -2
- package/dist/types/services/task/types.d.ts +125 -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 +11 -10
- package/src/cc.ts +221 -52
- package/src/constants.ts +1 -0
- package/src/index.ts +19 -3
- package/src/logger-proxy.ts +24 -1
- package/src/metrics/MetricsManager.ts +1 -1
- package/src/metrics/behavioral-events.ts +92 -0
- package/src/metrics/constants.ts +37 -1
- 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 +42 -2
- package/src/services/config/index.ts +30 -30
- package/src/services/config/types.ts +59 -58
- package/src/services/core/GlobalTypes.ts +27 -0
- package/src/services/core/Utils.ts +199 -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 +79 -3
- package/src/services/task/constants.ts +6 -0
- package/src/services/task/contact.ts +80 -0
- package/src/services/task/index.ts +457 -57
- package/src/services/task/types.ts +135 -0
- package/src/types.ts +180 -0
- package/src/utils/PageCache.ts +252 -0
- package/test/unit/spec/cc.ts +77 -84
- package/test/unit/spec/metrics/MetricsManager.ts +0 -1
- package/test/unit/spec/metrics/behavioral-events.ts +56 -0
- 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 +279 -65
- 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 +145 -1
- package/test/unit/spec/services/task/contact.ts +31 -1
- package/test/unit/spec/services/task/index.ts +410 -123
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/test/unit/spec/cc.ts
CHANGED
|
@@ -7,6 +7,11 @@ import {
|
|
|
7
7
|
WebexSDK,
|
|
8
8
|
} from '../../../src/types';
|
|
9
9
|
import ContactCenter from '../../../src/cc';
|
|
10
|
+
import EntryPoint from '../../../src/services/EntryPoint';
|
|
11
|
+
import type {EntryPointListResponse} from '../../../src/types';
|
|
12
|
+
import AddressBook from '../../../src/services/AddressBook';
|
|
13
|
+
import Queue from '../../../src/services/Queue';
|
|
14
|
+
import type {ContactServiceQueuesResponse} from '../../../src/types';
|
|
10
15
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
11
16
|
import {StationLoginSuccess, AGENT_EVENTS} from '../../../src/services/agent/types';
|
|
12
17
|
import {SetStateResponse} from '../../../src/types';
|
|
@@ -134,6 +139,7 @@ describe('webex.cc', () => {
|
|
|
134
139
|
webSocketManager: mockWebSocketManager,
|
|
135
140
|
task: undefined,
|
|
136
141
|
setWrapupData: jest.fn(),
|
|
142
|
+
setAgentId: jest.fn(),
|
|
137
143
|
registerIncomingCallEvent: jest.fn(),
|
|
138
144
|
registerTaskListeners: jest.fn(),
|
|
139
145
|
getTask: jest.fn(),
|
|
@@ -1310,7 +1316,7 @@ describe('webex.cc', () => {
|
|
|
1310
1316
|
});
|
|
1311
1317
|
|
|
1312
1318
|
describe('startOutdial', () => {
|
|
1313
|
-
it('should make outdial call successfully.', async () => {
|
|
1319
|
+
it('should make outdial call successfully without origin.', async () => {
|
|
1314
1320
|
// Setup outDialEp.
|
|
1315
1321
|
webex.cc.agentConfig = {
|
|
1316
1322
|
outDialEp: 'test-entry-point',
|
|
@@ -1319,9 +1325,10 @@ describe('webex.cc', () => {
|
|
|
1319
1325
|
// destination number required for making outdial call.
|
|
1320
1326
|
const destination = '1234567890';
|
|
1321
1327
|
|
|
1322
|
-
// Construct Payload for startOutdial.
|
|
1328
|
+
// Construct Payload for startOutdial without origin.
|
|
1323
1329
|
const newPayload = {
|
|
1324
1330
|
destination,
|
|
1331
|
+
origin: undefined,
|
|
1325
1332
|
entryPointId: 'test-entry-point',
|
|
1326
1333
|
direction: OUTDIAL_DIRECTION,
|
|
1327
1334
|
attributes: ATTRIBUTES,
|
|
@@ -1351,6 +1358,49 @@ describe('webex.cc', () => {
|
|
|
1351
1358
|
expect(result).toEqual(mockResponse);
|
|
1352
1359
|
});
|
|
1353
1360
|
|
|
1361
|
+
it('should make outdial call successfully with origin.', async () => {
|
|
1362
|
+
// Setup outDialEp.
|
|
1363
|
+
webex.cc.agentConfig = {
|
|
1364
|
+
outDialEp: 'test-entry-point',
|
|
1365
|
+
};
|
|
1366
|
+
|
|
1367
|
+
// destination number and origin for making outdial call.
|
|
1368
|
+
const destination = '1234567890';
|
|
1369
|
+
const origin = '+19403016307';
|
|
1370
|
+
|
|
1371
|
+
// Construct Payload for startOutdial with origin.
|
|
1372
|
+
const newPayload = {
|
|
1373
|
+
destination,
|
|
1374
|
+
origin,
|
|
1375
|
+
entryPointId: 'test-entry-point',
|
|
1376
|
+
direction: OUTDIAL_DIRECTION,
|
|
1377
|
+
attributes: ATTRIBUTES,
|
|
1378
|
+
mediaType: OUTDIAL_MEDIA_TYPE,
|
|
1379
|
+
outboundType: OUTBOUND_TYPE,
|
|
1380
|
+
} as const;
|
|
1381
|
+
|
|
1382
|
+
const mockResponse = {} as AgentContact;
|
|
1383
|
+
|
|
1384
|
+
const startOutdialMock = jest
|
|
1385
|
+
.spyOn(webex.cc.services.dialer, 'startOutdial')
|
|
1386
|
+
.mockResolvedValue(mockResponse);
|
|
1387
|
+
|
|
1388
|
+
const result = await webex.cc.startOutdial(destination, origin);
|
|
1389
|
+
|
|
1390
|
+
// Verify logging calls
|
|
1391
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Starting outbound dial', {
|
|
1392
|
+
module: CC_FILE,
|
|
1393
|
+
method: 'startOutdial',
|
|
1394
|
+
});
|
|
1395
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Outbound dial completed successfully', {
|
|
1396
|
+
module: CC_FILE,
|
|
1397
|
+
method: 'startOutdial',
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
expect(startOutdialMock).toHaveBeenCalledWith({data: newPayload});
|
|
1401
|
+
expect(result).toEqual(mockResponse);
|
|
1402
|
+
});
|
|
1403
|
+
|
|
1354
1404
|
it('should handle error during startOutdial', async () => {
|
|
1355
1405
|
// Setup outDialEp.
|
|
1356
1406
|
webex.cc.agentConfig = {
|
|
@@ -1387,88 +1437,6 @@ describe('webex.cc', () => {
|
|
|
1387
1437
|
});
|
|
1388
1438
|
});
|
|
1389
1439
|
|
|
1390
|
-
describe('getQueues', () => {
|
|
1391
|
-
it('should return queues response when successful', async () => {
|
|
1392
|
-
const mockQueuesResponse = [
|
|
1393
|
-
{
|
|
1394
|
-
queueId: 'queue1',
|
|
1395
|
-
queueName: 'Queue 1',
|
|
1396
|
-
},
|
|
1397
|
-
{
|
|
1398
|
-
queueId: 'queue2',
|
|
1399
|
-
queueName: 'Queue 2',
|
|
1400
|
-
},
|
|
1401
|
-
];
|
|
1402
|
-
|
|
1403
|
-
webex.cc.services.config.getQueues = jest.fn().mockResolvedValue(mockQueuesResponse);
|
|
1404
|
-
|
|
1405
|
-
const result = await webex.cc.getQueues();
|
|
1406
|
-
|
|
1407
|
-
// Verify logging calls
|
|
1408
|
-
expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching queues', {
|
|
1409
|
-
module: CC_FILE,
|
|
1410
|
-
method: 'getQueues',
|
|
1411
|
-
});
|
|
1412
|
-
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
1413
|
-
`Successfully retrieved ${result.length} queues`,
|
|
1414
|
-
{
|
|
1415
|
-
module: CC_FILE,
|
|
1416
|
-
method: 'getQueues',
|
|
1417
|
-
}
|
|
1418
|
-
);
|
|
1419
|
-
|
|
1420
|
-
expect(webex.cc.services.config.getQueues).toHaveBeenCalledWith(
|
|
1421
|
-
'mockOrgId',
|
|
1422
|
-
0,
|
|
1423
|
-
100,
|
|
1424
|
-
undefined,
|
|
1425
|
-
undefined
|
|
1426
|
-
);
|
|
1427
|
-
expect(result).toEqual(mockQueuesResponse);
|
|
1428
|
-
});
|
|
1429
|
-
|
|
1430
|
-
it('should throw an error if orgId is not present', async () => {
|
|
1431
|
-
jest.spyOn(webex.credentials, 'getOrgId').mockResolvedValue(undefined);
|
|
1432
|
-
webex.cc.services.config.getQueues = jest.fn();
|
|
1433
|
-
|
|
1434
|
-
try {
|
|
1435
|
-
await webex.cc.getQueues();
|
|
1436
|
-
} catch (error) {
|
|
1437
|
-
expect(error).toEqual(new Error('Org ID not found.'));
|
|
1438
|
-
expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching queues', {
|
|
1439
|
-
module: CC_FILE,
|
|
1440
|
-
method: 'getQueues',
|
|
1441
|
-
});
|
|
1442
|
-
expect(LoggerProxy.error).toHaveBeenCalledWith('Org ID not found.', {
|
|
1443
|
-
module: CC_FILE,
|
|
1444
|
-
method: 'getQueues',
|
|
1445
|
-
});
|
|
1446
|
-
expect(webex.cc.services.config.getQueues).not.toHaveBeenCalled();
|
|
1447
|
-
}
|
|
1448
|
-
});
|
|
1449
|
-
|
|
1450
|
-
it('should throw an error if config getQueues throws an error', async () => {
|
|
1451
|
-
webex.cc.services.config.getQueues = jest.fn().mockRejectedValue(new Error('Test error.'));
|
|
1452
|
-
|
|
1453
|
-
try {
|
|
1454
|
-
await webex.cc.getQueues();
|
|
1455
|
-
} catch (error) {
|
|
1456
|
-
expect(error).toEqual(new Error('Test error.'));
|
|
1457
|
-
expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching queues', {
|
|
1458
|
-
module: CC_FILE,
|
|
1459
|
-
method: 'getQueues',
|
|
1460
|
-
});
|
|
1461
|
-
expect(webex.cc.services.config.getQueues).toHaveBeenCalledWith(
|
|
1462
|
-
'mockOrgId',
|
|
1463
|
-
0,
|
|
1464
|
-
100,
|
|
1465
|
-
undefined,
|
|
1466
|
-
undefined
|
|
1467
|
-
);
|
|
1468
|
-
}
|
|
1469
|
-
});
|
|
1470
|
-
});
|
|
1471
|
-
|
|
1472
1440
|
describe('uploadLogs', () => {
|
|
1473
1441
|
it('should upload logs successfully', async () => {
|
|
1474
1442
|
const uploadLogsMock = jest.spyOn(webex.cc.webexRequest, 'uploadLogs').mockResolvedValue({
|
|
@@ -1796,6 +1764,31 @@ describe('webex.cc', () => {
|
|
|
1796
1764
|
});
|
|
1797
1765
|
});
|
|
1798
1766
|
|
|
1767
|
+
describe('API property exposure', () => {
|
|
1768
|
+
it('should provide getEntryPoints wrapper that delegates to EntryPoint', async () => {
|
|
1769
|
+
const spy = jest
|
|
1770
|
+
.spyOn(EntryPoint.prototype, 'getEntryPoints')
|
|
1771
|
+
.mockResolvedValue({} as EntryPointListResponse);
|
|
1772
|
+
await webex.cc.getEntryPoints();
|
|
1773
|
+
expect(spy).toHaveBeenCalled();
|
|
1774
|
+
spy.mockRestore();
|
|
1775
|
+
});
|
|
1776
|
+
|
|
1777
|
+
it('should expose addressBook API', () => {
|
|
1778
|
+
expect(webex.cc.addressBook).toBeDefined();
|
|
1779
|
+
expect(webex.cc.addressBook).toBeInstanceOf(AddressBook);
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
it('should provide getQueues wrapper that delegates to Queue', async () => {
|
|
1783
|
+
const spy = jest
|
|
1784
|
+
.spyOn(Queue.prototype, 'getQueues')
|
|
1785
|
+
.mockResolvedValue({} as ContactServiceQueuesResponse);
|
|
1786
|
+
await webex.cc.getQueues();
|
|
1787
|
+
expect(spy).toHaveBeenCalled();
|
|
1788
|
+
spy.mockRestore();
|
|
1789
|
+
});
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1799
1792
|
describe('updateAgentProfile', () => {
|
|
1800
1793
|
beforeEach(() => {
|
|
1801
1794
|
webex.cc.agentConfig = {
|
|
@@ -3,7 +3,6 @@ import MetricsManager from '../../../../src/metrics/MetricsManager';
|
|
|
3
3
|
import {METRIC_EVENT_NAMES} from '../../../../src/metrics/constants';
|
|
4
4
|
import {WebexSDK} from '../../../../src/types';
|
|
5
5
|
import {EventPayload} from '@webex/internal-plugin-metrics/src/metrics.types';
|
|
6
|
-
import LoggerProxy from '../../../../src/logger-proxy';
|
|
7
6
|
|
|
8
7
|
describe('MetricsManagerImplementation', () => {
|
|
9
8
|
let webex: WebexSDK;
|
|
@@ -12,6 +12,20 @@ describe('metrics/behavioral-events', () => {
|
|
|
12
12
|
verb: 'set',
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.AGENT_CONTACT_ASSIGN_FAILED)).toEqual({
|
|
16
|
+
product,
|
|
17
|
+
agent: 'service',
|
|
18
|
+
target: 'agent_contact_assign',
|
|
19
|
+
verb: 'fail',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.AGENT_INVITE_FAILED)).toEqual({
|
|
23
|
+
product,
|
|
24
|
+
agent: 'service',
|
|
25
|
+
target: 'agent_invite',
|
|
26
|
+
verb: 'fail',
|
|
27
|
+
});
|
|
28
|
+
|
|
15
29
|
expect(getEventTaxonomy(METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS)).toEqual({
|
|
16
30
|
product,
|
|
17
31
|
agent: 'user',
|
|
@@ -96,6 +110,48 @@ describe('metrics/behavioral-events', () => {
|
|
|
96
110
|
verb: 'fail',
|
|
97
111
|
});
|
|
98
112
|
|
|
113
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_START_SUCCESS)).toEqual({
|
|
114
|
+
product,
|
|
115
|
+
agent: 'user',
|
|
116
|
+
target: 'task_conference_start',
|
|
117
|
+
verb: 'complete',
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_START_FAILED)).toEqual({
|
|
121
|
+
product,
|
|
122
|
+
agent: 'user',
|
|
123
|
+
target: 'task_conference_start',
|
|
124
|
+
verb: 'fail',
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_END_SUCCESS)).toEqual({
|
|
128
|
+
product,
|
|
129
|
+
agent: 'user',
|
|
130
|
+
target: 'task_conference_end',
|
|
131
|
+
verb: 'complete',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_END_FAILED)).toEqual({
|
|
135
|
+
product,
|
|
136
|
+
agent: 'user',
|
|
137
|
+
target: 'task_conference_end',
|
|
138
|
+
verb: 'fail',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_SUCCESS)).toEqual({
|
|
142
|
+
product,
|
|
143
|
+
agent: 'user',
|
|
144
|
+
target: 'task_conference_transfer',
|
|
145
|
+
verb: 'complete',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_FAILED)).toEqual({
|
|
149
|
+
product,
|
|
150
|
+
agent: 'user',
|
|
151
|
+
target: 'task_conference_transfer',
|
|
152
|
+
verb: 'fail',
|
|
153
|
+
});
|
|
154
|
+
|
|
99
155
|
expect(getEventTaxonomy('' as METRIC_EVENT_NAMES)).toEqual(undefined);
|
|
100
156
|
});
|
|
101
157
|
});
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import AddressBook from '../../../../src/services/AddressBook';
|
|
2
|
+
import {HTTP_METHODS, WebexSDK, IHttpResponse} from '../../../../src/types';
|
|
3
|
+
import {METRIC_EVENT_NAMES} from '../../../../src/metrics/constants';
|
|
4
|
+
import WebexRequest from '../../../../src/services/core/WebexRequest';
|
|
5
|
+
import MetricsManager from '../../../../src/metrics/MetricsManager';
|
|
6
|
+
import LoggerProxy from '../../../../src/logger-proxy';
|
|
7
|
+
|
|
8
|
+
jest.mock('../../../../src/metrics/MetricsManager');
|
|
9
|
+
jest.mock('../../../../src/logger-proxy');
|
|
10
|
+
|
|
11
|
+
describe('AddressBook', () => {
|
|
12
|
+
let addressBookAPI: AddressBook;
|
|
13
|
+
let mockWebex: WebexSDK;
|
|
14
|
+
let mockMetricsManager: jest.Mocked<MetricsManager>;
|
|
15
|
+
|
|
16
|
+
const mockGetAddressBookId = jest.fn().mockReturnValue('test-address-book-id');
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
jest.clearAllMocks();
|
|
20
|
+
(WebexRequest as any).instance = undefined;
|
|
21
|
+
|
|
22
|
+
mockWebex = {
|
|
23
|
+
credentials: {
|
|
24
|
+
getOrgId: jest.fn().mockReturnValue('test-org-id'),
|
|
25
|
+
},
|
|
26
|
+
request: jest.fn(),
|
|
27
|
+
internal: {
|
|
28
|
+
newMetrics: {
|
|
29
|
+
submitBehavioralEvent: jest.fn(),
|
|
30
|
+
submitOperationalEvent: jest.fn(),
|
|
31
|
+
submitBusinessEvent: jest.fn(),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
ready: true,
|
|
35
|
+
once: jest.fn(),
|
|
36
|
+
} as unknown as WebexSDK;
|
|
37
|
+
|
|
38
|
+
mockMetricsManager = {
|
|
39
|
+
trackEvent: jest.fn(),
|
|
40
|
+
timeEvent: jest.fn(),
|
|
41
|
+
} as unknown as jest.Mocked<MetricsManager>;
|
|
42
|
+
(MetricsManager.getInstance as jest.Mock).mockReturnValue(mockMetricsManager);
|
|
43
|
+
|
|
44
|
+
addressBookAPI = new AddressBook(mockWebex, mockGetAddressBookId);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('constructor', () => {
|
|
48
|
+
it('should initialize with all required dependencies', () => {
|
|
49
|
+
expect(WebexRequest.getInstance({webex: mockWebex})).toBeDefined();
|
|
50
|
+
expect(MetricsManager.getInstance).toHaveBeenCalledWith({webex: mockWebex});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('getEntries', () => {
|
|
55
|
+
const mockEntries = [
|
|
56
|
+
{
|
|
57
|
+
id: 'entry1',
|
|
58
|
+
name: 'John Doe',
|
|
59
|
+
number: '+1234567890',
|
|
60
|
+
organizationId: 'test-org-id',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'entry2',
|
|
64
|
+
name: 'Jane Smith',
|
|
65
|
+
number: '+0987654321',
|
|
66
|
+
organizationId: 'test-org-id',
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const mockResponse: IHttpResponse = {
|
|
71
|
+
statusCode: 200,
|
|
72
|
+
method: 'GET',
|
|
73
|
+
url: '/organization/test-org-id/v2/address-book/test-address-book-id/entry',
|
|
74
|
+
headers: {} as any,
|
|
75
|
+
body: {
|
|
76
|
+
data: mockEntries,
|
|
77
|
+
meta: {
|
|
78
|
+
page: 0,
|
|
79
|
+
pageSize: 100,
|
|
80
|
+
totalPages: 1,
|
|
81
|
+
totalRecords: 2,
|
|
82
|
+
orgid: 'test-org-id',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
jest.spyOn(Date, 'now').mockReturnValue(1000);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
afterEach(() => {
|
|
92
|
+
jest.restoreAllMocks();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should fetch address book entries successfully with default parameters', async () => {
|
|
96
|
+
(mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
97
|
+
|
|
98
|
+
const result = await addressBookAPI.getEntries();
|
|
99
|
+
|
|
100
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
101
|
+
service: 'wcc-api-gateway',
|
|
102
|
+
resource: '/organization/test-org-id/v2/address-book/test-address-book-id/entry?page=0&pageSize=100',
|
|
103
|
+
method: HTTP_METHODS.GET,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(result).toEqual(mockResponse.body);
|
|
107
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_SUCCESS);
|
|
108
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
109
|
+
METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_SUCCESS,
|
|
110
|
+
{
|
|
111
|
+
orgId: 'test-org-id',
|
|
112
|
+
bookId: 'test-address-book-id',
|
|
113
|
+
statusCode: 200,
|
|
114
|
+
recordCount: 2,
|
|
115
|
+
totalRecords: 2,
|
|
116
|
+
isSearchRequest: false,
|
|
117
|
+
isFirstPage: true,
|
|
118
|
+
},
|
|
119
|
+
['behavioral', 'operational']
|
|
120
|
+
);
|
|
121
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching address book entries', {
|
|
122
|
+
module: 'AddressBook',
|
|
123
|
+
method: 'getEntries',
|
|
124
|
+
data: expect.objectContaining({
|
|
125
|
+
orgId: 'test-org-id',
|
|
126
|
+
bookId: 'test-address-book-id',
|
|
127
|
+
page: 0,
|
|
128
|
+
pageSize: 100,
|
|
129
|
+
isSearchRequest: false,
|
|
130
|
+
}),
|
|
131
|
+
});
|
|
132
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Making API request to fetch address book entries', {
|
|
133
|
+
module: 'AddressBook',
|
|
134
|
+
method: 'getEntries',
|
|
135
|
+
data: {
|
|
136
|
+
resource: '/organization/test-org-id/v2/address-book/test-address-book-id/entry?page=0&pageSize=100',
|
|
137
|
+
service: 'wcc-api-gateway',
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should fetch entries with custom parameters', async () => {
|
|
143
|
+
(mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
144
|
+
|
|
145
|
+
const params = {
|
|
146
|
+
addressBookId: 'custom-book-id',
|
|
147
|
+
page: 1,
|
|
148
|
+
pageSize: 25,
|
|
149
|
+
search: 'john',
|
|
150
|
+
filter: 'name=="John Doe"',
|
|
151
|
+
attributes: 'id,name,number',
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const result = await addressBookAPI.getEntries(params);
|
|
155
|
+
|
|
156
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
157
|
+
service: 'wcc-api-gateway',
|
|
158
|
+
resource: '/organization/test-org-id/v2/address-book/custom-book-id/entry?page=1&pageSize=25&filter=name%3D%3D%22John+Doe%22&attributes=id%2Cname%2Cnumber&search=john',
|
|
159
|
+
method: HTTP_METHODS.GET,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(result).toEqual(mockResponse.body);
|
|
163
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
164
|
+
METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_SUCCESS,
|
|
165
|
+
{
|
|
166
|
+
orgId: 'test-org-id',
|
|
167
|
+
bookId: 'custom-book-id',
|
|
168
|
+
statusCode: 200,
|
|
169
|
+
recordCount: 2,
|
|
170
|
+
totalRecords: 2,
|
|
171
|
+
isSearchRequest: true,
|
|
172
|
+
isFirstPage: false,
|
|
173
|
+
},
|
|
174
|
+
['behavioral', 'operational']
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should handle API errors and track metrics', async () => {
|
|
179
|
+
(mockWebex.request as jest.Mock).mockRejectedValue(new Error('Internal Server Error'));
|
|
180
|
+
|
|
181
|
+
await expect(addressBookAPI.getEntries()).rejects.toThrow('Internal Server Error');
|
|
182
|
+
|
|
183
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
184
|
+
service: 'wcc-api-gateway',
|
|
185
|
+
resource: '/organization/test-org-id/v2/address-book/test-address-book-id/entry?page=0&pageSize=100',
|
|
186
|
+
method: HTTP_METHODS.GET,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
190
|
+
METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_FAILED,
|
|
191
|
+
expect.objectContaining({
|
|
192
|
+
orgId: 'test-org-id',
|
|
193
|
+
bookId: 'test-address-book-id',
|
|
194
|
+
error: 'Internal Server Error',
|
|
195
|
+
isSearchRequest: false,
|
|
196
|
+
page: 0,
|
|
197
|
+
pageSize: 100,
|
|
198
|
+
}),
|
|
199
|
+
['behavioral', 'operational']
|
|
200
|
+
);
|
|
201
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to fetch address book entries', {
|
|
202
|
+
module: 'AddressBook',
|
|
203
|
+
method: 'getEntries',
|
|
204
|
+
data: expect.objectContaining({
|
|
205
|
+
orgId: 'test-org-id',
|
|
206
|
+
bookId: 'test-address-book-id',
|
|
207
|
+
error: 'Internal Server Error',
|
|
208
|
+
isSearchRequest: false,
|
|
209
|
+
page: 0,
|
|
210
|
+
pageSize: 100,
|
|
211
|
+
}),
|
|
212
|
+
error: expect.any(Error),
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should not track metrics for subsequent pages in simple pagination', async () => {
|
|
217
|
+
const mockResponsePage2: IHttpResponse = {
|
|
218
|
+
statusCode: 200,
|
|
219
|
+
method: 'GET',
|
|
220
|
+
url: '/organization/test-org-id/v2/address-book/test-address-book-id/entry',
|
|
221
|
+
headers: {} as any,
|
|
222
|
+
body: {
|
|
223
|
+
data: mockEntries,
|
|
224
|
+
meta: {
|
|
225
|
+
page: 2,
|
|
226
|
+
pageSize: 100,
|
|
227
|
+
totalPages: 3,
|
|
228
|
+
totalRecords: 2,
|
|
229
|
+
orgid: 'test-org-id',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
(mockWebex.request as jest.Mock).mockResolvedValueOnce(mockResponsePage2);
|
|
235
|
+
|
|
236
|
+
const result = await addressBookAPI.getEntries({page: 2});
|
|
237
|
+
|
|
238
|
+
expect(result).toEqual(mockResponsePage2.body);
|
|
239
|
+
expect(mockMetricsManager.trackEvent).not.toHaveBeenCalledWith(
|
|
240
|
+
METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_SUCCESS,
|
|
241
|
+
expect.any(Object),
|
|
242
|
+
expect.any(Array)
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should track metrics for search requests on a valid non-first page', async () => {
|
|
247
|
+
(mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
248
|
+
|
|
249
|
+
const result = await addressBookAPI.getEntries({page: 1, search: 'test'});
|
|
250
|
+
expect(result).toEqual(mockResponse.body);
|
|
251
|
+
|
|
252
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
253
|
+
METRIC_EVENT_NAMES.ADDRESSBOOK_FETCH_SUCCESS,
|
|
254
|
+
{
|
|
255
|
+
orgId: 'test-org-id',
|
|
256
|
+
bookId: 'test-address-book-id',
|
|
257
|
+
statusCode: 200,
|
|
258
|
+
recordCount: 2,
|
|
259
|
+
totalRecords: 2,
|
|
260
|
+
isSearchRequest: true,
|
|
261
|
+
isFirstPage: false,
|
|
262
|
+
},
|
|
263
|
+
['behavioral', 'operational']
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should return cached data for repeat simple pagination calls', async () => {
|
|
268
|
+
(mockWebex.request as jest.Mock).mockResolvedValueOnce(mockResponse);
|
|
269
|
+
|
|
270
|
+
const first = await addressBookAPI.getEntries({page: 0});
|
|
271
|
+
expect(first).toEqual(mockResponse.body);
|
|
272
|
+
|
|
273
|
+
const callsBefore = (mockWebex.request as jest.Mock).mock.calls.length;
|
|
274
|
+
const second = await addressBookAPI.getEntries({page: 0});
|
|
275
|
+
const callsAfter = (mockWebex.request as jest.Mock).mock.calls.length;
|
|
276
|
+
|
|
277
|
+
expect(second.data).toEqual(mockResponse.body.data);
|
|
278
|
+
expect(second.meta).toEqual(
|
|
279
|
+
expect.objectContaining({
|
|
280
|
+
page: 0,
|
|
281
|
+
pageSize: 100,
|
|
282
|
+
totalPages: 1,
|
|
283
|
+
totalRecords: 2,
|
|
284
|
+
})
|
|
285
|
+
);
|
|
286
|
+
expect(callsAfter).toBe(callsBefore);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should call API when requested page is not cached (cache miss)', async () => {
|
|
290
|
+
(mockWebex.request as jest.Mock).mockResolvedValueOnce(mockResponse);
|
|
291
|
+
|
|
292
|
+
await addressBookAPI.getEntries({page: 0});
|
|
293
|
+
|
|
294
|
+
(mockWebex.request as jest.Mock).mockResolvedValueOnce({
|
|
295
|
+
...mockResponse,
|
|
296
|
+
body: {
|
|
297
|
+
data: mockEntries,
|
|
298
|
+
meta: {
|
|
299
|
+
page: 1,
|
|
300
|
+
pageSize: 100,
|
|
301
|
+
totalPages: 2,
|
|
302
|
+
totalRecords: 2,
|
|
303
|
+
orgid: 'test-org-id',
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const callsBefore = (mockWebex.request as jest.Mock).mock.calls.length;
|
|
309
|
+
(LoggerProxy.info as jest.Mock).mockClear();
|
|
310
|
+
(LoggerProxy.log as jest.Mock).mockClear();
|
|
311
|
+
|
|
312
|
+
const result = await addressBookAPI.getEntries({page: 1});
|
|
313
|
+
|
|
314
|
+
const callsAfter = (mockWebex.request as jest.Mock).mock.calls.length;
|
|
315
|
+
expect(callsAfter).toBe(callsBefore + 1);
|
|
316
|
+
expect(result.meta.page).toBe(1);
|
|
317
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
318
|
+
service: 'wcc-api-gateway',
|
|
319
|
+
resource: '/organization/test-org-id/v2/address-book/test-address-book-id/entry?page=1&pageSize=100',
|
|
320
|
+
method: HTTP_METHODS.GET,
|
|
321
|
+
});
|
|
322
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Making API request to fetch address book entries', {
|
|
323
|
+
module: 'AddressBook',
|
|
324
|
+
method: 'getEntries',
|
|
325
|
+
data: {
|
|
326
|
+
resource: '/organization/test-org-id/v2/address-book/test-address-book-id/entry?page=1&pageSize=100',
|
|
327
|
+
service: 'wcc-api-gateway',
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
});
|