@webex/contact-center 3.8.1-next.41 → 3.9.0-multipleLLM.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/cc.js +105 -62
  2. package/dist/cc.js.map +1 -1
  3. package/dist/index.js +9 -0
  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 +76 -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 +3 -2
  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 +9 -9
  65. package/src/cc.ts +121 -80
  66. package/src/index.ts +16 -2
  67. package/src/logger-proxy.ts +24 -1
  68. package/src/metrics/MetricsManager.ts +1 -1
  69. package/src/metrics/behavioral-events.ts +80 -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 +42 -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
@@ -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
+ });
@@ -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
+ });
@@ -961,75 +961,4 @@ describe('AgentConfigService', () => {
961
961
  );
962
962
  });
963
963
  });
964
-
965
- describe('getQueues', () => {
966
- const mockQueues = [
967
- {id: 'queue1', name: 'Queue 1'},
968
- {id: 'queue2', name: 'Queue 2'},
969
- ];
970
-
971
- const mockError = new Error('API call failed');
972
-
973
- beforeEach(() => {
974
- jest.clearAllMocks();
975
- });
976
-
977
- it('should return a list of queues successfully', async () => {
978
- const mockResponse = {statusCode: 200, body: {data: mockQueues}};
979
- mockWebexRequest.request.mockResolvedValue(mockResponse);
980
-
981
- const result = await agentConfigService.getQueues(mockOrgId, 0, 100, 'queue', 'id==someid');
982
-
983
- expect(mockWebexRequest.request).toHaveBeenCalledWith({
984
- service: mockWccAPIURL,
985
- resource: `organization/${mockOrgId}/v2/contact-service-queue?page=0&pageSize=100&desktopProfileFilter=true&search=queue&filter=id==someid`,
986
- method: 'GET',
987
- });
988
- expect(result).toEqual(mockQueues);
989
-
990
- expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching queue list', {
991
- module: CONFIG_FILE_NAME,
992
- method: 'getQueues',
993
- });
994
- expect(LoggerProxy.log).toHaveBeenCalledWith('getQueues API success.', {
995
- module: CONFIG_FILE_NAME,
996
- method: 'getQueues',
997
- });
998
- });
999
-
1000
- it('should throw an error if the API call fails', async () => {
1001
- mockWebexRequest.request.mockRejectedValue(mockError);
1002
-
1003
- await expect(agentConfigService.getQueues(mockOrgId, 0, 100)).rejects.toThrow(
1004
- 'API call failed'
1005
- );
1006
- expect(LoggerProxy.error).toHaveBeenCalledWith(
1007
- 'getQueues API call failed with Error: API call failed',
1008
- {module: CONFIG_FILE_NAME, method: 'getQueues'}
1009
- );
1010
- expect(mockWebexRequest.request).toHaveBeenCalledWith({
1011
- service: mockWccAPIURL,
1012
- resource: `organization/${mockOrgId}/v2/contact-service-queue?page=0&pageSize=100&desktopProfileFilter=true`,
1013
- method: 'GET',
1014
- });
1015
- });
1016
-
1017
- it('should throw an error if the API call returns a non-200 status code', async () => {
1018
- const mockResponse = {statusCode: 500};
1019
- mockWebexRequest.request.mockResolvedValue(mockResponse);
1020
-
1021
- await expect(agentConfigService.getQueues(mockOrgId, 0, 100)).rejects.toThrow(
1022
- 'API call failed with 500'
1023
- );
1024
- expect(LoggerProxy.error).toHaveBeenCalledWith(
1025
- 'getQueues API call failed with Error: API call failed with 500',
1026
- {module: CONFIG_FILE_NAME, method: 'getQueues'}
1027
- );
1028
- expect(mockWebexRequest.request).toHaveBeenCalledWith({
1029
- service: mockWccAPIURL,
1030
- resource: `organization/${mockOrgId}/v2/contact-service-queue?page=0&pageSize=100&desktopProfileFilter=true`,
1031
- method: 'GET',
1032
- });
1033
- });
1034
- });
1035
964
  });