@webex/contact-center 3.8.1 → 3.9.0-multipleLLM.2
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 +106 -63
- package/dist/cc.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 +26 -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 +22 -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 +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 +77 -43
- package/dist/types/index.d.ts +8 -3
- package/dist/types/metrics/constants.d.ts +20 -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 +44 -64
- 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 +123 -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 +10 -10
- package/src/cc.ts +122 -81
- 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 +30 -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 +22 -65
- 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 +133 -0
- package/src/types.ts +180 -0
- package/src/utils/PageCache.ts +252 -0
- package/test/unit/spec/cc.ts +31 -82
- 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 +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 +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(),
|
|
@@ -1387,88 +1393,6 @@ describe('webex.cc', () => {
|
|
|
1387
1393
|
});
|
|
1388
1394
|
});
|
|
1389
1395
|
|
|
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
1396
|
describe('uploadLogs', () => {
|
|
1473
1397
|
it('should upload logs successfully', async () => {
|
|
1474
1398
|
const uploadLogsMock = jest.spyOn(webex.cc.webexRequest, 'uploadLogs').mockResolvedValue({
|
|
@@ -1796,6 +1720,31 @@ describe('webex.cc', () => {
|
|
|
1796
1720
|
});
|
|
1797
1721
|
});
|
|
1798
1722
|
|
|
1723
|
+
describe('API property exposure', () => {
|
|
1724
|
+
it('should provide getEntryPoints wrapper that delegates to EntryPoint', async () => {
|
|
1725
|
+
const spy = jest
|
|
1726
|
+
.spyOn(EntryPoint.prototype, 'getEntryPoints')
|
|
1727
|
+
.mockResolvedValue({} as EntryPointListResponse);
|
|
1728
|
+
await webex.cc.getEntryPoints();
|
|
1729
|
+
expect(spy).toHaveBeenCalled();
|
|
1730
|
+
spy.mockRestore();
|
|
1731
|
+
});
|
|
1732
|
+
|
|
1733
|
+
it('should expose addressBook API', () => {
|
|
1734
|
+
expect(webex.cc.addressBook).toBeDefined();
|
|
1735
|
+
expect(webex.cc.addressBook).toBeInstanceOf(AddressBook);
|
|
1736
|
+
});
|
|
1737
|
+
|
|
1738
|
+
it('should provide getQueues wrapper that delegates to Queue', async () => {
|
|
1739
|
+
const spy = jest
|
|
1740
|
+
.spyOn(Queue.prototype, 'getQueues')
|
|
1741
|
+
.mockResolvedValue({} as ContactServiceQueuesResponse);
|
|
1742
|
+
await webex.cc.getQueues();
|
|
1743
|
+
expect(spy).toHaveBeenCalled();
|
|
1744
|
+
spy.mockRestore();
|
|
1745
|
+
});
|
|
1746
|
+
});
|
|
1747
|
+
|
|
1799
1748
|
describe('updateAgentProfile', () => {
|
|
1800
1749
|
beforeEach(() => {
|
|
1801
1750
|
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
|
+
});
|