@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.
Files changed (102) hide show
  1. package/dist/cc.js +106 -63
  2. package/dist/cc.js.map +1 -1
  3. package/dist/index.js +13 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/logger-proxy.js +24 -1
  6. package/dist/logger-proxy.js.map +1 -1
  7. package/dist/metrics/MetricsManager.js +1 -1
  8. package/dist/metrics/MetricsManager.js.map +1 -1
  9. package/dist/metrics/behavioral-events.js +88 -0
  10. package/dist/metrics/behavioral-events.js.map +1 -1
  11. package/dist/metrics/constants.js +26 -1
  12. package/dist/metrics/constants.js.map +1 -1
  13. package/dist/services/AddressBook.js +271 -0
  14. package/dist/services/AddressBook.js.map +1 -0
  15. package/dist/services/EntryPoint.js +227 -0
  16. package/dist/services/EntryPoint.js.map +1 -0
  17. package/dist/services/Queue.js +261 -0
  18. package/dist/services/Queue.js.map +1 -0
  19. package/dist/services/config/constants.js +24 -2
  20. package/dist/services/config/constants.js.map +1 -1
  21. package/dist/services/config/index.js +1 -43
  22. package/dist/services/config/index.js.map +1 -1
  23. package/dist/services/config/types.js +22 -5
  24. package/dist/services/config/types.js.map +1 -1
  25. package/dist/services/core/GlobalTypes.js.map +1 -1
  26. package/dist/services/core/Utils.js +162 -2
  27. package/dist/services/core/Utils.js.map +1 -1
  28. package/dist/services/core/aqm-reqs.js +0 -4
  29. package/dist/services/core/aqm-reqs.js.map +1 -1
  30. package/dist/services/core/websocket/WebSocketManager.js +0 -4
  31. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  32. package/dist/services/task/TaskManager.js +74 -2
  33. package/dist/services/task/TaskManager.js.map +1 -1
  34. package/dist/services/task/constants.js +7 -1
  35. package/dist/services/task/constants.js.map +1 -1
  36. package/dist/services/task/contact.js +86 -0
  37. package/dist/services/task/contact.js.map +1 -1
  38. package/dist/services/task/index.js +384 -72
  39. package/dist/services/task/index.js.map +1 -1
  40. package/dist/services/task/types.js +14 -0
  41. package/dist/services/task/types.js.map +1 -1
  42. package/dist/types/cc.d.ts +77 -43
  43. package/dist/types/index.d.ts +8 -3
  44. package/dist/types/metrics/constants.d.ts +20 -0
  45. package/dist/types/services/AddressBook.d.ts +74 -0
  46. package/dist/types/services/EntryPoint.d.ts +67 -0
  47. package/dist/types/services/Queue.d.ts +76 -0
  48. package/dist/types/services/config/constants.d.ts +23 -1
  49. package/dist/types/services/config/index.d.ts +1 -14
  50. package/dist/types/services/config/types.d.ts +44 -64
  51. package/dist/types/services/core/GlobalTypes.d.ts +25 -0
  52. package/dist/types/services/core/Utils.d.ts +40 -1
  53. package/dist/types/services/task/constants.d.ts +6 -0
  54. package/dist/types/services/task/contact.d.ts +10 -0
  55. package/dist/types/services/task/index.d.ts +44 -2
  56. package/dist/types/services/task/types.d.ts +123 -1
  57. package/dist/types/types.d.ts +162 -0
  58. package/dist/types/utils/PageCache.d.ts +173 -0
  59. package/dist/types.js +17 -0
  60. package/dist/types.js.map +1 -1
  61. package/dist/utils/PageCache.js +192 -0
  62. package/dist/utils/PageCache.js.map +1 -0
  63. package/dist/webex.js +1 -1
  64. package/package.json +10 -10
  65. package/src/cc.ts +122 -81
  66. package/src/index.ts +19 -3
  67. package/src/logger-proxy.ts +24 -1
  68. package/src/metrics/MetricsManager.ts +1 -1
  69. package/src/metrics/behavioral-events.ts +92 -0
  70. package/src/metrics/constants.ts +30 -0
  71. package/src/services/AddressBook.ts +291 -0
  72. package/src/services/EntryPoint.ts +241 -0
  73. package/src/services/Queue.ts +277 -0
  74. package/src/services/config/constants.ts +26 -2
  75. package/src/services/config/index.ts +1 -55
  76. package/src/services/config/types.ts +22 -65
  77. package/src/services/core/GlobalTypes.ts +27 -0
  78. package/src/services/core/Utils.ts +199 -1
  79. package/src/services/core/aqm-reqs.ts +0 -5
  80. package/src/services/core/websocket/WebSocketManager.ts +0 -4
  81. package/src/services/task/TaskManager.ts +79 -3
  82. package/src/services/task/constants.ts +6 -0
  83. package/src/services/task/contact.ts +80 -0
  84. package/src/services/task/index.ts +457 -57
  85. package/src/services/task/types.ts +133 -0
  86. package/src/types.ts +180 -0
  87. package/src/utils/PageCache.ts +252 -0
  88. package/test/unit/spec/cc.ts +31 -82
  89. package/test/unit/spec/metrics/MetricsManager.ts +0 -1
  90. package/test/unit/spec/metrics/behavioral-events.ts +56 -0
  91. package/test/unit/spec/services/AddressBook.ts +332 -0
  92. package/test/unit/spec/services/EntryPoint.ts +259 -0
  93. package/test/unit/spec/services/Queue.ts +323 -0
  94. package/test/unit/spec/services/config/index.ts +0 -71
  95. package/test/unit/spec/services/core/Utils.ts +50 -0
  96. package/test/unit/spec/services/core/aqm-reqs.ts +1 -3
  97. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +0 -4
  98. package/test/unit/spec/services/task/TaskManager.ts +145 -1
  99. package/test/unit/spec/services/task/contact.ts +31 -1
  100. package/test/unit/spec/services/task/index.ts +410 -123
  101. package/umd/contact-center.min.js +2 -2
  102. package/umd/contact-center.min.js.map +1 -1
@@ -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
+ });