@webex/contact-center 3.9.0 → 3.10.0-next.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 (111) hide show
  1. package/dist/cc.js +196 -47
  2. package/dist/cc.js.map +1 -1
  3. package/dist/constants.js +1 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/index.js +9 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/logger-proxy.js +24 -1
  8. package/dist/logger-proxy.js.map +1 -1
  9. package/dist/metrics/MetricsManager.js +1 -1
  10. package/dist/metrics/MetricsManager.js.map +1 -1
  11. package/dist/metrics/behavioral-events.js +89 -0
  12. package/dist/metrics/behavioral-events.js.map +1 -1
  13. package/dist/metrics/constants.js +32 -2
  14. package/dist/metrics/constants.js.map +1 -1
  15. package/dist/services/AddressBook.js +271 -0
  16. package/dist/services/AddressBook.js.map +1 -0
  17. package/dist/services/EntryPoint.js +227 -0
  18. package/dist/services/EntryPoint.js.map +1 -0
  19. package/dist/services/Queue.js +261 -0
  20. package/dist/services/Queue.js.map +1 -0
  21. package/dist/services/config/constants.js +36 -2
  22. package/dist/services/config/constants.js.map +1 -1
  23. package/dist/services/config/index.js +29 -21
  24. package/dist/services/config/index.js.map +1 -1
  25. package/dist/services/config/types.js +33 -1
  26. package/dist/services/config/types.js.map +1 -1
  27. package/dist/services/core/GlobalTypes.js.map +1 -1
  28. package/dist/services/core/Utils.js +162 -2
  29. package/dist/services/core/Utils.js.map +1 -1
  30. package/dist/services/core/aqm-reqs.js +0 -4
  31. package/dist/services/core/aqm-reqs.js.map +1 -1
  32. package/dist/services/core/websocket/WebSocketManager.js +0 -4
  33. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  34. package/dist/services/task/TaskManager.js +114 -3
  35. package/dist/services/task/TaskManager.js.map +1 -1
  36. package/dist/services/task/TaskUtils.js +76 -0
  37. package/dist/services/task/TaskUtils.js.map +1 -0
  38. package/dist/services/task/constants.js +26 -1
  39. package/dist/services/task/constants.js.map +1 -1
  40. package/dist/services/task/contact.js +86 -0
  41. package/dist/services/task/contact.js.map +1 -1
  42. package/dist/services/task/index.js +418 -87
  43. package/dist/services/task/index.js.map +1 -1
  44. package/dist/services/task/types.js +14 -0
  45. package/dist/services/task/types.js.map +1 -1
  46. package/dist/types/cc.d.ts +115 -35
  47. package/dist/types/constants.d.ts +1 -0
  48. package/dist/types/index.d.ts +3 -2
  49. package/dist/types/metrics/constants.d.ts +25 -1
  50. package/dist/types/services/AddressBook.d.ts +74 -0
  51. package/dist/types/services/EntryPoint.d.ts +67 -0
  52. package/dist/types/services/Queue.d.ts +76 -0
  53. package/dist/types/services/config/constants.d.ts +35 -1
  54. package/dist/types/services/config/index.d.ts +6 -9
  55. package/dist/types/services/config/types.d.ts +79 -58
  56. package/dist/types/services/core/GlobalTypes.d.ts +25 -0
  57. package/dist/types/services/core/Utils.d.ts +40 -1
  58. package/dist/types/services/task/TaskUtils.d.ts +28 -0
  59. package/dist/types/services/task/constants.d.ts +23 -0
  60. package/dist/types/services/task/contact.d.ts +10 -0
  61. package/dist/types/services/task/index.d.ts +85 -4
  62. package/dist/types/services/task/types.d.ts +233 -21
  63. package/dist/types/types.d.ts +162 -0
  64. package/dist/types/utils/PageCache.d.ts +173 -0
  65. package/dist/types.js +17 -0
  66. package/dist/types.js.map +1 -1
  67. package/dist/utils/PageCache.js +192 -0
  68. package/dist/utils/PageCache.js.map +1 -0
  69. package/dist/webex.js +1 -1
  70. package/package.json +10 -9
  71. package/src/cc.ts +221 -52
  72. package/src/constants.ts +1 -0
  73. package/src/index.ts +16 -2
  74. package/src/logger-proxy.ts +24 -1
  75. package/src/metrics/MetricsManager.ts +1 -1
  76. package/src/metrics/behavioral-events.ts +94 -0
  77. package/src/metrics/constants.ts +37 -1
  78. package/src/services/AddressBook.ts +291 -0
  79. package/src/services/EntryPoint.ts +241 -0
  80. package/src/services/Queue.ts +277 -0
  81. package/src/services/config/constants.ts +42 -2
  82. package/src/services/config/index.ts +30 -30
  83. package/src/services/config/types.ts +59 -58
  84. package/src/services/core/GlobalTypes.ts +27 -0
  85. package/src/services/core/Utils.ts +199 -1
  86. package/src/services/core/aqm-reqs.ts +0 -5
  87. package/src/services/core/websocket/WebSocketManager.ts +0 -4
  88. package/src/services/task/TaskManager.ts +123 -5
  89. package/src/services/task/TaskUtils.ts +81 -0
  90. package/src/services/task/constants.ts +25 -0
  91. package/src/services/task/contact.ts +80 -0
  92. package/src/services/task/index.ts +510 -71
  93. package/src/services/task/types.ts +251 -20
  94. package/src/types.ts +180 -0
  95. package/src/utils/PageCache.ts +252 -0
  96. package/test/unit/spec/cc.ts +282 -85
  97. package/test/unit/spec/metrics/MetricsManager.ts +0 -1
  98. package/test/unit/spec/metrics/behavioral-events.ts +42 -0
  99. package/test/unit/spec/services/AddressBook.ts +332 -0
  100. package/test/unit/spec/services/EntryPoint.ts +259 -0
  101. package/test/unit/spec/services/Queue.ts +323 -0
  102. package/test/unit/spec/services/config/index.ts +279 -65
  103. package/test/unit/spec/services/core/Utils.ts +50 -0
  104. package/test/unit/spec/services/core/aqm-reqs.ts +1 -3
  105. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +0 -4
  106. package/test/unit/spec/services/task/TaskManager.ts +390 -1
  107. package/test/unit/spec/services/task/TaskUtils.ts +131 -0
  108. package/test/unit/spec/services/task/contact.ts +31 -1
  109. package/test/unit/spec/services/task/index.ts +585 -130
  110. package/umd/contact-center.min.js +2 -2
  111. package/umd/contact-center.min.js.map +1 -1
@@ -110,6 +110,48 @@ describe('metrics/behavioral-events', () => {
110
110
  verb: 'fail',
111
111
  });
112
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
+
113
155
  expect(getEventTaxonomy('' as METRIC_EVENT_NAMES)).toEqual(undefined);
114
156
  });
115
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
+ });
@@ -0,0 +1,259 @@
1
+ import EntryPoint from '../../../../src/services/EntryPoint';
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('EntryPoint', () => {
12
+ let entryPointAPI: EntryPoint;
13
+ let mockWebex: WebexSDK;
14
+ let mockMetricsManager: jest.Mocked<MetricsManager>;
15
+
16
+ beforeEach(() => {
17
+ jest.clearAllMocks();
18
+
19
+ (WebexRequest as any).instance = undefined;
20
+ mockWebex = {
21
+ credentials: {
22
+ getOrgId: jest.fn().mockReturnValue('test-org-id'),
23
+ },
24
+ request: jest.fn(),
25
+ internal: {
26
+ newMetrics: {
27
+ submitBehavioralEvent: jest.fn(),
28
+ submitOperationalEvent: jest.fn(),
29
+ submitBusinessEvent: jest.fn(),
30
+ },
31
+ },
32
+ ready: true,
33
+ once: jest.fn(),
34
+ } as unknown as WebexSDK;
35
+
36
+ mockMetricsManager = {
37
+ trackEvent: jest.fn(),
38
+ timeEvent: jest.fn(),
39
+ } as unknown as jest.Mocked<MetricsManager>;
40
+ (MetricsManager.getInstance as jest.Mock).mockReturnValue(mockMetricsManager);
41
+
42
+ entryPointAPI = new EntryPoint(mockWebex);
43
+ });
44
+
45
+ describe('constructor', () => {
46
+ it('should initialize with all required dependencies', () => {
47
+ expect(WebexRequest.getInstance({webex: mockWebex})).toBeDefined();
48
+ expect(MetricsManager.getInstance).toHaveBeenCalledWith({webex: mockWebex});
49
+ });
50
+ });
51
+
52
+ describe('getEntryPoints', () => {
53
+ const mockEntryPoints = [
54
+ {
55
+ id: 'entry1',
56
+ name: 'Test Entry Point 1',
57
+ type: 'voice',
58
+ isActive: true,
59
+ orgId: 'test-org-id',
60
+ },
61
+ {
62
+ id: 'entry2',
63
+ name: 'Test Entry Point 2',
64
+ type: 'chat',
65
+ isActive: true,
66
+ orgId: 'test-org-id',
67
+ },
68
+ ];
69
+
70
+ const mockResponse: IHttpResponse = {
71
+ statusCode: 200,
72
+ method: 'GET',
73
+ url: '/organization/test-org-id/v2/entry-point',
74
+ headers: {} as any,
75
+ body: {
76
+ data: mockEntryPoints,
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 entry points successfully with default parameters', async () => {
96
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
97
+
98
+ const result = await entryPointAPI.getEntryPoints();
99
+
100
+ expect(mockWebex.request).toHaveBeenCalledWith({
101
+ service: 'wcc-api-gateway',
102
+ resource: '/organization/test-org-id/v2/entry-point?page=0&pageSize=100&sortOrder=asc',
103
+ method: HTTP_METHODS.GET,
104
+ });
105
+
106
+ expect(result).toEqual(mockResponse.body);
107
+ expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(METRIC_EVENT_NAMES.ENTRYPOINT_FETCH_SUCCESS);
108
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
109
+ METRIC_EVENT_NAMES.ENTRYPOINT_FETCH_SUCCESS,
110
+ {
111
+ orgId: 'test-org-id',
112
+ statusCode: 200,
113
+ recordCount: 2,
114
+ totalRecords: 2,
115
+ isSearchRequest: false,
116
+ isFirstPage: true,
117
+ },
118
+ ['behavioral']
119
+ );
120
+ expect(LoggerProxy.info).toHaveBeenCalledWith(
121
+ 'Fetching entry points - orgId: test-org-id, page: 0, pageSize: 100, isSearchRequest: false',
122
+ {module: 'EntryPoint', method: 'getEntryPoints'}
123
+ );
124
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
125
+ `Making API request to fetch entry points - resource: /organization/test-org-id/v2/entry-point?page=0&pageSize=100&sortOrder=asc, service: wcc-api-gateway`,
126
+ {module: 'EntryPoint', method: 'getEntryPoints'}
127
+ );
128
+ });
129
+
130
+ it('should fetch entry points with custom parameters', async () => {
131
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
132
+
133
+ const params = {
134
+ page: 1,
135
+ pageSize: 25,
136
+ search: 'test',
137
+ filter: 'type=="voice"',
138
+ attributes: 'id,name',
139
+ sortBy: 'name',
140
+ sortOrder: 'desc' as const,
141
+ };
142
+
143
+ await entryPointAPI.getEntryPoints(params);
144
+
145
+ expect(mockWebex.request).toHaveBeenCalledWith({
146
+ service: 'wcc-api-gateway',
147
+ resource: '/organization/test-org-id/v2/entry-point?page=1&pageSize=25&sortOrder=desc&search=test&filter=type%3D%3D%22voice%22&attributes=id%2Cname&sortBy=name',
148
+ method: HTTP_METHODS.GET,
149
+ });
150
+
151
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
152
+ METRIC_EVENT_NAMES.ENTRYPOINT_FETCH_SUCCESS,
153
+ {
154
+ orgId: 'test-org-id',
155
+ statusCode: 200,
156
+ recordCount: 2,
157
+ totalRecords: 2,
158
+ isSearchRequest: true,
159
+ isFirstPage: false,
160
+ },
161
+ ['behavioral']
162
+ );
163
+ });
164
+
165
+ it('should handle API errors and track metrics', async () => {
166
+ (mockWebex.request as jest.Mock).mockRejectedValue(new Error('Internal Server Error'));
167
+
168
+ await expect(entryPointAPI.getEntryPoints()).rejects.toThrow('Internal Server Error');
169
+
170
+ expect(mockWebex.request).toHaveBeenCalledWith({
171
+ service: 'wcc-api-gateway',
172
+ resource: '/organization/test-org-id/v2/entry-point?page=0&pageSize=100&sortOrder=asc',
173
+ method: HTTP_METHODS.GET,
174
+ });
175
+
176
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
177
+ METRIC_EVENT_NAMES.ENTRYPOINT_FETCH_FAILED,
178
+ {
179
+ orgId: 'test-org-id',
180
+ error: 'Internal Server Error',
181
+ isSearchRequest: false,
182
+ page: 0,
183
+ pageSize: 100,
184
+ },
185
+ ['behavioral']
186
+ );
187
+ expect(LoggerProxy.error).toHaveBeenCalled();
188
+ });
189
+
190
+
191
+
192
+ it('should not track metrics for subsequent pages in simple pagination', async () => {
193
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
194
+
195
+ const result = await entryPointAPI.getEntryPoints({page: 2});
196
+ expect(result).toEqual(mockResponse.body);
197
+
198
+ expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
199
+ });
200
+
201
+ it('should track metrics for search requests on any page', async () => {
202
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
203
+
204
+ const result2 = await entryPointAPI.getEntryPoints({page: 2, search: 'test'});
205
+ expect(result2).toEqual(mockResponse.body);
206
+
207
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
208
+ METRIC_EVENT_NAMES.ENTRYPOINT_FETCH_SUCCESS,
209
+ {
210
+ orgId: 'test-org-id',
211
+ statusCode: 200,
212
+ recordCount: 2,
213
+ totalRecords: 2,
214
+ isSearchRequest: true,
215
+ isFirstPage: false,
216
+ },
217
+ ['behavioral']
218
+ );
219
+ });
220
+
221
+ it('should call API when requested page is not cached (cache miss)', async () => {
222
+ (mockWebex.request as jest.Mock).mockResolvedValueOnce(mockResponse);
223
+
224
+ await entryPointAPI.getEntryPoints({page: 0});
225
+
226
+ (mockWebex.request as jest.Mock).mockResolvedValueOnce({
227
+ ...mockResponse,
228
+ body: {
229
+ data: mockEntryPoints,
230
+ meta: {
231
+ page: 1,
232
+ pageSize: 100,
233
+ totalPages: 2,
234
+ totalRecords: 2,
235
+ orgid: 'test-org-id',
236
+ },
237
+ },
238
+ });
239
+
240
+ const callsBefore = (mockWebex.request as jest.Mock).mock.calls.length;
241
+ (LoggerProxy.log as jest.Mock).mockClear();
242
+
243
+ const result = await entryPointAPI.getEntryPoints({page: 1});
244
+ const callsAfter = (mockWebex.request as jest.Mock).mock.calls.length;
245
+
246
+ expect(callsAfter).toBe(callsBefore + 1);
247
+ expect(result.meta.page).toBe(1);
248
+ expect(mockWebex.request).toHaveBeenCalledWith({
249
+ service: 'wcc-api-gateway',
250
+ resource: '/organization/test-org-id/v2/entry-point?page=1&pageSize=100&sortOrder=asc',
251
+ method: HTTP_METHODS.GET,
252
+ });
253
+ expect(LoggerProxy.log).toHaveBeenCalledWith(
254
+ `Making API request to fetch entry points - resource: /organization/test-org-id/v2/entry-point?page=1&pageSize=100&sortOrder=asc, service: wcc-api-gateway`,
255
+ {module: 'EntryPoint', method: 'getEntryPoints'}
256
+ );
257
+ });
258
+ });
259
+ });