@webex/contact-center 3.9.0-next.2 → 3.9.0-next.21

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 (106) hide show
  1. package/dist/cc.js +195 -46
  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 +74 -2
  35. package/dist/services/task/TaskManager.js.map +1 -1
  36. package/dist/services/task/constants.js +7 -1
  37. package/dist/services/task/constants.js.map +1 -1
  38. package/dist/services/task/contact.js +86 -0
  39. package/dist/services/task/contact.js.map +1 -1
  40. package/dist/services/task/index.js +384 -72
  41. package/dist/services/task/index.js.map +1 -1
  42. package/dist/services/task/types.js +14 -0
  43. package/dist/services/task/types.js.map +1 -1
  44. package/dist/types/cc.d.ts +115 -35
  45. package/dist/types/constants.d.ts +1 -0
  46. package/dist/types/index.d.ts +3 -2
  47. package/dist/types/metrics/constants.d.ts +25 -1
  48. package/dist/types/services/AddressBook.d.ts +74 -0
  49. package/dist/types/services/EntryPoint.d.ts +67 -0
  50. package/dist/types/services/Queue.d.ts +76 -0
  51. package/dist/types/services/config/constants.d.ts +35 -1
  52. package/dist/types/services/config/index.d.ts +6 -9
  53. package/dist/types/services/config/types.d.ts +79 -58
  54. package/dist/types/services/core/GlobalTypes.d.ts +25 -0
  55. package/dist/types/services/core/Utils.d.ts +40 -1
  56. package/dist/types/services/task/constants.d.ts +6 -0
  57. package/dist/types/services/task/contact.d.ts +10 -0
  58. package/dist/types/services/task/index.d.ts +44 -2
  59. package/dist/types/services/task/types.d.ts +125 -1
  60. package/dist/types/types.d.ts +162 -0
  61. package/dist/types/utils/PageCache.d.ts +173 -0
  62. package/dist/types.js +17 -0
  63. package/dist/types.js.map +1 -1
  64. package/dist/utils/PageCache.js +192 -0
  65. package/dist/utils/PageCache.js.map +1 -0
  66. package/dist/webex.js +1 -1
  67. package/package.json +9 -8
  68. package/src/cc.ts +220 -51
  69. package/src/constants.ts +1 -0
  70. package/src/index.ts +16 -2
  71. package/src/logger-proxy.ts +24 -1
  72. package/src/metrics/MetricsManager.ts +1 -1
  73. package/src/metrics/behavioral-events.ts +94 -0
  74. package/src/metrics/constants.ts +37 -1
  75. package/src/services/AddressBook.ts +291 -0
  76. package/src/services/EntryPoint.ts +241 -0
  77. package/src/services/Queue.ts +277 -0
  78. package/src/services/config/constants.ts +42 -2
  79. package/src/services/config/index.ts +30 -30
  80. package/src/services/config/types.ts +59 -58
  81. package/src/services/core/GlobalTypes.ts +27 -0
  82. package/src/services/core/Utils.ts +199 -1
  83. package/src/services/core/aqm-reqs.ts +0 -5
  84. package/src/services/core/websocket/WebSocketManager.ts +0 -4
  85. package/src/services/task/TaskManager.ts +79 -3
  86. package/src/services/task/constants.ts +6 -0
  87. package/src/services/task/contact.ts +80 -0
  88. package/src/services/task/index.ts +457 -57
  89. package/src/services/task/types.ts +135 -0
  90. package/src/types.ts +180 -0
  91. package/src/utils/PageCache.ts +252 -0
  92. package/test/unit/spec/cc.ts +282 -85
  93. package/test/unit/spec/metrics/MetricsManager.ts +0 -1
  94. package/test/unit/spec/metrics/behavioral-events.ts +42 -0
  95. package/test/unit/spec/services/AddressBook.ts +332 -0
  96. package/test/unit/spec/services/EntryPoint.ts +259 -0
  97. package/test/unit/spec/services/Queue.ts +323 -0
  98. package/test/unit/spec/services/config/index.ts +279 -65
  99. package/test/unit/spec/services/core/Utils.ts +50 -0
  100. package/test/unit/spec/services/core/aqm-reqs.ts +1 -3
  101. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +0 -4
  102. package/test/unit/spec/services/task/TaskManager.ts +145 -1
  103. package/test/unit/spec/services/task/contact.ts +31 -1
  104. package/test/unit/spec/services/task/index.ts +410 -123
  105. package/umd/contact-center.min.js +2 -2
  106. package/umd/contact-center.min.js.map +1 -1
@@ -0,0 +1,323 @@
1
+ import Queue from '../../../../src/services/Queue';
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('Queue', () => {
12
+ let queueAPI: Queue;
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
+ queueAPI = new Queue(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('getQueues', () => {
53
+ const mockQueues = [
54
+ {
55
+ id: 'queue1',
56
+ name: 'Support Queue',
57
+ queueType: 'INBOUND' as const,
58
+ channelType: 'TELEPHONY' as const,
59
+ active: true,
60
+ organizationId: 'test-org-id',
61
+ checkAgentAvailability: true,
62
+ serviceLevelThreshold: 300,
63
+ maxActiveContacts: 10,
64
+ maxTimeInQueue: 3600,
65
+ defaultMusicInQueueMediaFileId: 'media123',
66
+ monitoringPermitted: true,
67
+ parkingPermitted: true,
68
+ recordingPermitted: true,
69
+ recordingAllCallsPermitted: false,
70
+ pauseRecordingPermitted: true,
71
+ controlFlowScriptUrl: 'https://example.com/script',
72
+ ivrRequeueUrl: 'https://example.com/requeue',
73
+ routingType: 'LONGEST_AVAILABLE_AGENT' as const,
74
+ queueRoutingType: 'TEAM_BASED' as const,
75
+ callDistributionGroups: [
76
+ {
77
+ agentGroups: [{teamId: 'team1'}],
78
+ order: 1,
79
+ duration: 30
80
+ }
81
+ ],
82
+ },
83
+ {
84
+ id: 'queue2',
85
+ name: 'Sales Queue',
86
+ queueType: 'INBOUND' as const,
87
+ channelType: 'CHAT' as const,
88
+ active: true,
89
+ organizationId: 'test-org-id',
90
+ checkAgentAvailability: true,
91
+ serviceLevelThreshold: 300,
92
+ maxActiveContacts: 5,
93
+ maxTimeInQueue: 1800,
94
+ defaultMusicInQueueMediaFileId: 'media456',
95
+ monitoringPermitted: true,
96
+ parkingPermitted: false,
97
+ recordingPermitted: false,
98
+ recordingAllCallsPermitted: false,
99
+ pauseRecordingPermitted: false,
100
+ controlFlowScriptUrl: 'https://example.com/script2',
101
+ ivrRequeueUrl: 'https://example.com/requeue2',
102
+ routingType: 'SKILLS_BASED' as const,
103
+ queueRoutingType: 'SKILL_BASED' as const,
104
+ callDistributionGroups: [
105
+ {
106
+ agentGroups: [{teamId: 'team2'}],
107
+ order: 1,
108
+ duration: 60
109
+ }
110
+ ],
111
+ },
112
+ ];
113
+
114
+ const mockResponse: IHttpResponse = {
115
+ statusCode: 200,
116
+ method: 'GET',
117
+ url: '/organization/test-org-id/v2/contact-service-queue',
118
+ headers: {} as any,
119
+ body: {
120
+ data: mockQueues,
121
+ meta: {
122
+ page: 0,
123
+ pageSize: 100,
124
+ totalPages: 1,
125
+ totalRecords: 2,
126
+ orgid: 'test-org-id',
127
+ },
128
+ },
129
+ };
130
+
131
+ beforeEach(() => {
132
+ jest.spyOn(Date, 'now').mockReturnValue(1000);
133
+ });
134
+
135
+ afterEach(() => {
136
+ jest.restoreAllMocks();
137
+ });
138
+
139
+ it('should fetch contact service queues successfully with default parameters', async () => {
140
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
141
+
142
+ const result = await queueAPI.getQueues();
143
+
144
+ expect(mockWebex.request).toHaveBeenCalledWith({
145
+ service: 'wcc-api-gateway',
146
+ resource: '/organization/test-org-id/v2/contact-service-queue?page=0&pageSize=100',
147
+ method: HTTP_METHODS.GET,
148
+ });
149
+
150
+ expect(result).toEqual(mockResponse.body);
151
+ expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(METRIC_EVENT_NAMES.QUEUE_FETCH_SUCCESS);
152
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
153
+ METRIC_EVENT_NAMES.QUEUE_FETCH_SUCCESS,
154
+ {
155
+ orgId: 'test-org-id',
156
+ statusCode: 200,
157
+ recordCount: 2,
158
+ totalRecords: 2,
159
+ isSearchRequest: false,
160
+ isFirstPage: true,
161
+ },
162
+ ['behavioral', 'operational']
163
+ );
164
+ expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching contact service queues', {
165
+ module: 'Queue',
166
+ method: 'getQueues',
167
+ data: expect.objectContaining({
168
+ orgId: 'test-org-id',
169
+ page: 0,
170
+ pageSize: 100,
171
+ isSearchRequest: false,
172
+ }),
173
+ });
174
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Making API request to fetch contact service queues', {
175
+ module: 'Queue',
176
+ method: 'getQueues',
177
+ data: expect.objectContaining({
178
+ resource: '/organization/test-org-id/v2/contact-service-queue?page=0&pageSize=100',
179
+ service: 'wcc-api-gateway',
180
+ }),
181
+ });
182
+ });
183
+
184
+ it('should fetch queues with custom parameters', async () => {
185
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
186
+
187
+ const params = {
188
+ page: 1,
189
+ pageSize: 25,
190
+ search: 'support',
191
+ filter: 'queueType=="INBOUND"',
192
+ attributes: 'id,name,queueType',
193
+ sortBy: 'name',
194
+ sortOrder: 'desc' as const,
195
+ desktopProfileFilter: true,
196
+ provisioningView: false,
197
+ singleObjectResponse: true,
198
+ };
199
+
200
+ await queueAPI.getQueues(params);
201
+
202
+ expect(mockWebex.request).toHaveBeenCalledWith({
203
+ service: 'wcc-api-gateway',
204
+ resource: '/organization/test-org-id/v2/contact-service-queue?page=1&pageSize=25&filter=queueType%3D%3D%22INBOUND%22&attributes=id%2Cname%2CqueueType&search=support&sortBy=name&sortOrder=desc&desktopProfileFilter=true&provisioningView=false&singleObjectResponse=true',
205
+ method: HTTP_METHODS.GET,
206
+ });
207
+
208
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
209
+ METRIC_EVENT_NAMES.QUEUE_FETCH_SUCCESS,
210
+ expect.objectContaining({
211
+ isSearchRequest: true,
212
+ isFirstPage: false,
213
+ }),
214
+ ['behavioral', 'operational']
215
+ );
216
+ });
217
+
218
+ it('should handle API errors and track metrics', async () => {
219
+ (mockWebex.request as jest.Mock).mockRejectedValue(new Error('Internal Server Error'));
220
+
221
+ await expect(queueAPI.getQueues()).rejects.toThrow('Internal Server Error');
222
+
223
+ expect(mockWebex.request).toHaveBeenCalledWith({
224
+ service: 'wcc-api-gateway',
225
+ resource: '/organization/test-org-id/v2/contact-service-queue?page=0&pageSize=100',
226
+ method: HTTP_METHODS.GET,
227
+ });
228
+
229
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
230
+ METRIC_EVENT_NAMES.QUEUE_FETCH_FAILED,
231
+ {
232
+ orgId: 'test-org-id',
233
+ error: 'Internal Server Error',
234
+ isSearchRequest: false,
235
+ page: 0,
236
+ pageSize: 100,
237
+ },
238
+ ['behavioral', 'operational']
239
+ );
240
+ expect(LoggerProxy.error).toHaveBeenCalledWith('Failed to fetch contact service queues', {
241
+ module: 'Queue',
242
+ method: 'getQueues',
243
+ data: expect.objectContaining({
244
+ orgId: 'test-org-id',
245
+ error: 'Internal Server Error',
246
+ isSearchRequest: false,
247
+ page: 0,
248
+ pageSize: 100,
249
+ }),
250
+ error: expect.any(Error),
251
+ });
252
+ });
253
+
254
+
255
+
256
+ it('should not track metrics for subsequent pages in simple pagination', async () => {
257
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
258
+
259
+ const result = await queueAPI.getQueues({page: 2});
260
+ expect(result).toEqual(mockResponse.body);
261
+
262
+ expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
263
+ });
264
+
265
+ it('should track metrics for search requests on any page', async () => {
266
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
267
+
268
+ const result2 = await queueAPI.getQueues({page: 2, search: 'test'});
269
+ expect(result2).toEqual(mockResponse.body);
270
+
271
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
272
+ METRIC_EVENT_NAMES.QUEUE_FETCH_SUCCESS,
273
+ expect.objectContaining({
274
+ isSearchRequest: true,
275
+ isFirstPage: false,
276
+ }),
277
+ ['behavioral', 'operational']
278
+ );
279
+ });
280
+
281
+ it('should call API when requested page is not cached (cache miss)', async () => {
282
+ (mockWebex.request as jest.Mock).mockResolvedValueOnce(mockResponse);
283
+
284
+ await queueAPI.getQueues({page: 0});
285
+
286
+ (mockWebex.request as jest.Mock).mockResolvedValueOnce({
287
+ ...mockResponse,
288
+ body: {
289
+ data: mockQueues,
290
+ meta: {
291
+ page: 1,
292
+ pageSize: 100,
293
+ totalPages: 2,
294
+ totalRecords: 2,
295
+ orgid: 'test-org-id',
296
+ },
297
+ },
298
+ });
299
+
300
+ const callsBefore = (mockWebex.request as jest.Mock).mock.calls.length;
301
+ (LoggerProxy.log as jest.Mock).mockClear();
302
+
303
+ const result = await queueAPI.getQueues({page: 1});
304
+ const callsAfter = (mockWebex.request as jest.Mock).mock.calls.length;
305
+
306
+ expect(callsAfter).toBe(callsBefore + 1);
307
+ expect(result.meta.page).toBe(1);
308
+ expect(mockWebex.request).toHaveBeenCalledWith({
309
+ service: 'wcc-api-gateway',
310
+ resource: '/organization/test-org-id/v2/contact-service-queue?page=1&pageSize=100',
311
+ method: HTTP_METHODS.GET,
312
+ });
313
+ expect(LoggerProxy.log).toHaveBeenCalledWith('Making API request to fetch contact service queues', {
314
+ module: 'Queue',
315
+ method: 'getQueues',
316
+ data: expect.objectContaining({
317
+ resource: '/organization/test-org-id/v2/contact-service-queue?page=1&pageSize=100',
318
+ service: 'wcc-api-gateway',
319
+ }),
320
+ });
321
+ });
322
+ });
323
+ });