@webex/contact-center 3.12.0-task-refactor.11 → 3.12.0-task-refactor.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cc.js +297 -83
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/metrics/behavioral-events.js +39 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +8 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +3 -0
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +1 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +10 -0
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +1 -0
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +57 -6
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +92 -17
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +24 -3
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +6 -0
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskManager.js +155 -6
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +39 -11
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +129 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +30 -3
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/actions.js +4 -0
- package/dist/services/task/state-machine/actions.js.map +1 -1
- package/dist/services/task/state-machine/constants.js +3 -0
- package/dist/services/task/state-machine/constants.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +23 -0
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/state-machine/types.js.map +1 -1
- package/dist/services/task/types.js +59 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +76 -2
- package/dist/types/constants.d.ts +3 -0
- package/dist/types/metrics/constants.d.ts +6 -0
- package/dist/types/services/config/types.d.ts +22 -1
- package/dist/types/services/core/Err.d.ts +6 -0
- package/dist/types/services/core/Utils.d.ts +20 -2
- package/dist/types/services/core/aqm-reqs.d.ts +49 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +1 -0
- package/dist/types/services/task/Task.d.ts +1 -0
- package/dist/types/services/task/TaskUtils.d.ts +13 -0
- package/dist/types/services/task/constants.d.ts +6 -0
- package/dist/types/services/task/dialer.d.ts +45 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +36 -8
- package/dist/types/services/task/state-machine/constants.d.ts +3 -0
- package/dist/types/services/task/state-machine/guards.d.ts +2 -0
- package/dist/types/services/task/state-machine/types.d.ts +11 -0
- package/dist/types/services/task/types.d.ts +75 -1
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/cc.ts +389 -111
- package/src/constants.ts +3 -0
- package/src/metrics/behavioral-events.ts +42 -0
- package/src/metrics/constants.ts +9 -1
- package/src/services/ApiAiAssistant.ts +3 -0
- package/src/services/config/Util.ts +1 -1
- package/src/services/config/types.ts +12 -1
- package/src/services/constants.ts +1 -0
- package/src/services/core/Err.ts +3 -0
- package/src/services/core/Utils.ts +68 -5
- package/src/services/core/aqm-reqs.ts +100 -22
- package/src/services/core/websocket/WebSocketManager.ts +23 -2
- package/src/services/index.ts +1 -0
- package/src/services/task/Task.ts +13 -0
- package/src/services/task/TaskManager.ts +171 -7
- package/src/services/task/TaskUtils.ts +33 -1
- package/src/services/task/constants.ts +6 -0
- package/src/services/task/dialer.ts +136 -1
- package/src/services/task/state-machine/TaskStateMachine.ts +37 -5
- package/src/services/task/state-machine/actions.ts +4 -0
- package/src/services/task/state-machine/constants.ts +3 -0
- package/src/services/task/state-machine/guards.ts +30 -0
- package/src/services/task/state-machine/types.ts +14 -1
- package/src/services/task/types.ts +80 -0
- package/src/types.ts +4 -1
- package/test/unit/spec/cc.ts +294 -32
- package/test/unit/spec/services/config/index.ts +30 -2
- package/test/unit/spec/services/core/Utils.ts +367 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +91 -1
- package/test/unit/spec/services/core/websocket/connection-service.ts +4 -5
- package/test/unit/spec/services/task/TaskManager.ts +4 -2
- package/test/unit/spec/services/task/dialer.ts +372 -96
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -463,6 +463,48 @@ const eventTaxonomyMap: Record<string, BehavioralEventTaxonomy> = {
|
|
|
463
463
|
target: 'outdial_ani_ep_fetch',
|
|
464
464
|
verb: 'fail',
|
|
465
465
|
},
|
|
466
|
+
|
|
467
|
+
// Campaign Preview API Events
|
|
468
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS]: {
|
|
469
|
+
product,
|
|
470
|
+
agent: 'user',
|
|
471
|
+
target: 'campaign_preview_accept',
|
|
472
|
+
verb: 'complete',
|
|
473
|
+
},
|
|
474
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED]: {
|
|
475
|
+
product,
|
|
476
|
+
agent: 'user',
|
|
477
|
+
target: 'campaign_preview_accept',
|
|
478
|
+
verb: 'fail',
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
// Campaign Preview Skip API Events
|
|
482
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS]: {
|
|
483
|
+
product,
|
|
484
|
+
agent: 'user',
|
|
485
|
+
target: 'campaign_preview_skip',
|
|
486
|
+
verb: 'complete',
|
|
487
|
+
},
|
|
488
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED]: {
|
|
489
|
+
product,
|
|
490
|
+
agent: 'user',
|
|
491
|
+
target: 'campaign_preview_skip',
|
|
492
|
+
verb: 'fail',
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
// Campaign Preview Remove API Events
|
|
496
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS]: {
|
|
497
|
+
product,
|
|
498
|
+
agent: 'user',
|
|
499
|
+
target: 'campaign_preview_remove',
|
|
500
|
+
verb: 'complete',
|
|
501
|
+
},
|
|
502
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED]: {
|
|
503
|
+
product,
|
|
504
|
+
agent: 'user',
|
|
505
|
+
target: 'campaign_preview_remove',
|
|
506
|
+
verb: 'fail',
|
|
507
|
+
},
|
|
466
508
|
};
|
|
467
509
|
|
|
468
510
|
/**
|
package/src/metrics/constants.ts
CHANGED
|
@@ -163,7 +163,15 @@ export const METRIC_EVENT_NAMES = {
|
|
|
163
163
|
OUTDIAL_ANI_EP_FETCH_SUCCESS: 'Outdial ANI Entries Fetch Success',
|
|
164
164
|
OUTDIAL_ANI_EP_FETCH_FAILED: 'Outdial ANI Entries Fetch Failed',
|
|
165
165
|
|
|
166
|
-
//
|
|
166
|
+
// Campaign Preview API Events
|
|
167
|
+
CAMPAIGN_PREVIEW_ACCEPT_SUCCESS: 'Campaign Preview Accept Success',
|
|
168
|
+
CAMPAIGN_PREVIEW_ACCEPT_FAILED: 'Campaign Preview Accept Failed',
|
|
169
|
+
CAMPAIGN_PREVIEW_SKIP_SUCCESS: 'Campaign Preview Skip Success',
|
|
170
|
+
CAMPAIGN_PREVIEW_SKIP_FAILED: 'Campaign Preview Skip Failed',
|
|
171
|
+
CAMPAIGN_PREVIEW_REMOVE_SUCCESS: 'Campaign Preview Remove Success',
|
|
172
|
+
CAMPAIGN_PREVIEW_REMOVE_FAILED: 'Campaign Preview Remove Failed',
|
|
173
|
+
|
|
174
|
+
// AI Assistant Transcript events
|
|
167
175
|
AI_ASSISTANT_SEND_EVENT_SUCCESS: 'AI Assistant Send Event Success',
|
|
168
176
|
AI_ASSISTANT_SEND_EVENT_FAILED: 'AI Assistant Send Event Failed',
|
|
169
177
|
AI_ASSISTANT_GET_SUGGESTED_RESPONSE_SUCCESS: 'AI Assistant Get Suggested Response Success',
|
|
@@ -189,7 +189,7 @@ function parseAgentConfigs(profileData: {
|
|
|
189
189
|
|
|
190
190
|
const finalData = {
|
|
191
191
|
teams: teamData,
|
|
192
|
-
defaultDn: userData.
|
|
192
|
+
defaultDn: userData.deafultDialledNumber,
|
|
193
193
|
forceDefaultDn: tenantData.forceDefaultDn,
|
|
194
194
|
forceDefaultDnForAgent: getDefaultAgentDN(agentProfileData.agentDNValidation),
|
|
195
195
|
regexUS: tenantData.dnDefaultRegex,
|
|
@@ -119,6 +119,16 @@ export const CC_TASK_EVENTS = {
|
|
|
119
119
|
AGENT_CONTACT_UNASSIGNED: 'AgentContactUnassigned',
|
|
120
120
|
/** Event emitted when inviting agent fails */
|
|
121
121
|
AGENT_INVITE_FAILED: 'AgentInviteFailed',
|
|
122
|
+
/** Event emitted when a campaign preview contact is offered to the agent */
|
|
123
|
+
AGENT_OFFER_CAMPAIGN_RESERVATION: 'AgentOfferCampaignReservation',
|
|
124
|
+
/** Event emitted when campaign contact is updated */
|
|
125
|
+
CAMPAIGN_CONTACT_UPDATED: 'CampaignContactUpdated',
|
|
126
|
+
/** Event emitted when accepting a campaign preview contact fails */
|
|
127
|
+
CAMPAIGN_PREVIEW_ACCEPT_FAILED: 'CampaignPreviewAcceptFailed',
|
|
128
|
+
/** Event emitted when skipping a campaign preview contact fails */
|
|
129
|
+
CAMPAIGN_PREVIEW_SKIP_FAILED: 'CampaignPreviewSkipFailed',
|
|
130
|
+
/** Event emitted when removing a campaign preview contact fails */
|
|
131
|
+
CAMPAIGN_PREVIEW_REMOVE_FAILED: 'CampaignPreviewRemoveFailed',
|
|
122
132
|
/** Event emitted when a real-time transcript chunk is received */
|
|
123
133
|
REAL_TIME_TRANSCRIPTION: 'REAL_TIME_TRANSCRIPTION',
|
|
124
134
|
/** Event emitted when an AI assistant suggested response is available */
|
|
@@ -294,8 +304,9 @@ export type AgentResponse = {
|
|
|
294
304
|
|
|
295
305
|
/**
|
|
296
306
|
* The default dialed number of the agent.
|
|
307
|
+
* Note: The API returns this field as "deafultDialledNumber" (with typo).
|
|
297
308
|
*/
|
|
298
|
-
|
|
309
|
+
deafultDialledNumber?: string;
|
|
299
310
|
};
|
|
300
311
|
|
|
301
312
|
/**
|
package/src/services/core/Err.ts
CHANGED
|
@@ -38,6 +38,9 @@ export type TaskErrorIds =
|
|
|
38
38
|
| {'Service.aqm.task.pauseRecording': Failure}
|
|
39
39
|
| {'Service.aqm.task.resumeRecording': Failure}
|
|
40
40
|
| {'Service.aqm.dialer.startOutdial': Failure}
|
|
41
|
+
| {'Service.aqm.dialer.acceptPreviewContact': Failure}
|
|
42
|
+
| {'Service.aqm.dialer.skipPreviewContact': Failure}
|
|
43
|
+
| {'Service.aqm.dialer.removePreviewContact': Failure}
|
|
41
44
|
| {'Service.reqs.generic.failure': {trackingId: string}};
|
|
42
45
|
|
|
43
46
|
export type ReqError =
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
InteractionParticipant,
|
|
15
15
|
} from '../task/types';
|
|
16
16
|
import {PARTICIPANT_TYPES, STATE_CONSULT} from './constants';
|
|
17
|
+
import {DialPlan} from '../config/types';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Extracts common error details from a Webex request payload.
|
|
@@ -30,11 +31,72 @@ const getCommonErrorDetails = (errObj: WebexRequestPayload) => {
|
|
|
30
31
|
};
|
|
31
32
|
};
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Strips characters defined in the dial plan entry from the input string.
|
|
36
|
+
*
|
|
37
|
+
* @param input - The dial number to sanitize
|
|
38
|
+
* @param strippedChars - String of characters to remove from the input
|
|
39
|
+
* @returns The sanitized input with specified characters removed
|
|
40
|
+
*/
|
|
41
|
+
export const stripDialPlanChars = (input: string, strippedChars: string): string => {
|
|
42
|
+
if (!strippedChars) {
|
|
43
|
+
return input;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const charsToStrip = new Set(strippedChars.split(''));
|
|
36
47
|
|
|
37
|
-
return
|
|
48
|
+
return input
|
|
49
|
+
.split('')
|
|
50
|
+
.filter((c) => !charsToStrip.has(c))
|
|
51
|
+
.join('');
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Validates a dial number against the provided dial plan regex patterns.
|
|
56
|
+
* A number is valid if it matches at least one regex pattern in the dial plans.
|
|
57
|
+
* Skips validation when no dial plan entries are configured, deferring to the server.
|
|
58
|
+
*
|
|
59
|
+
* @param input - The dial number to validate
|
|
60
|
+
* @param dialPlanEntries - Array of dial plan entries containing regex patterns
|
|
61
|
+
* @returns true if the input matches at least one dial plan regex pattern or no entries are configured, false otherwise
|
|
62
|
+
*/
|
|
63
|
+
export const isValidDialNumber = (
|
|
64
|
+
input: string,
|
|
65
|
+
dialPlanEntries: DialPlan['dialPlanEntity']
|
|
66
|
+
): boolean => {
|
|
67
|
+
if (!input) {
|
|
68
|
+
LoggerProxy.warn('Dial number is empty or undefined.', {
|
|
69
|
+
module: 'Utils',
|
|
70
|
+
method: 'isValidDialNumber',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!dialPlanEntries || dialPlanEntries.length === 0) {
|
|
77
|
+
LoggerProxy.log(
|
|
78
|
+
'No dial plan entries found. Skipping client-side validation, deferring to server.',
|
|
79
|
+
{module: 'Utils', method: 'isValidDialNumber'}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return dialPlanEntries.some((entry) => {
|
|
86
|
+
try {
|
|
87
|
+
const sanitizedInput = stripDialPlanChars(input, entry.strippedChars);
|
|
88
|
+
const regex = new RegExp(entry.regex);
|
|
89
|
+
|
|
90
|
+
return regex.test(sanitizedInput);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
LoggerProxy.warn(`Failed to validate dial number against entry "${entry.name}": ${e}`, {
|
|
93
|
+
module: 'Utils',
|
|
94
|
+
method: 'isValidDialNumber',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
38
100
|
};
|
|
39
101
|
|
|
40
102
|
export const getStationLoginErrorData = (failure: Failure, loginOption: LoginOption) => {
|
|
@@ -56,7 +118,7 @@ export const getStationLoginErrorData = (failure: Failure, loginOption: LoginOpt
|
|
|
56
118
|
},
|
|
57
119
|
INVALID_DIAL_NUMBER: {
|
|
58
120
|
message:
|
|
59
|
-
'Enter a valid
|
|
121
|
+
'Enter a valid dial number. For help, reach out to your administrator or support team.',
|
|
60
122
|
fieldName: loginOption,
|
|
61
123
|
},
|
|
62
124
|
};
|
|
@@ -199,6 +261,7 @@ export const createErrDetailsObject = (errObj: WebexRequestPayload) => {
|
|
|
199
261
|
};
|
|
200
262
|
|
|
201
263
|
/*
|
|
264
|
+
/**
|
|
202
265
|
* Gets the consulted agent ID from the media object by finding the agent
|
|
203
266
|
* in the consult media participants (excluding the current agent).
|
|
204
267
|
*
|
|
@@ -20,18 +20,40 @@ export default class AqmReqs {
|
|
|
20
20
|
this.webSocketManager.on('message', this.onMessage.bind(this));
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Creates a request function for an API call with parameters
|
|
25
|
+
* @param c - The configuration for the request
|
|
26
|
+
* @returns A function that makes the API request
|
|
27
|
+
*/
|
|
23
28
|
req<TRes, TErr, TReq>(c: Conf<TRes, TErr, TReq>): Res<TRes, TReq> {
|
|
24
29
|
return (p: TReq, cbRes?: CbRes<TRes>) => this.makeAPIRequest(c(p), cbRes);
|
|
25
30
|
}
|
|
26
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Creates a request function for an API call with no parameters
|
|
34
|
+
* @param c - The configuration for the request
|
|
35
|
+
* @returns A function that makes the API request
|
|
36
|
+
*/
|
|
27
37
|
reqEmpty<TRes, TErr>(c: ConfEmpty<TRes, TErr>): ResEmpty<TRes> {
|
|
28
38
|
return (cbRes?: CbRes<TRes>) => this.makeAPIRequest(c(), cbRes);
|
|
29
39
|
}
|
|
30
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Makes an API request
|
|
43
|
+
* @param c - The request configuration
|
|
44
|
+
* @param cbRes - The callback for the response
|
|
45
|
+
* @returns A promise that resolves with the response or rejects with an error
|
|
46
|
+
*/
|
|
31
47
|
private async makeAPIRequest<TRes, TErr>(c: Req<TRes, TErr>, cbRes?: CbRes<TRes>): Promise<TRes> {
|
|
32
48
|
return this.createPromise(c, cbRes);
|
|
33
49
|
}
|
|
34
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Creates a promise for an API request
|
|
53
|
+
* @param c - The request configuration
|
|
54
|
+
* @param cbRes - The callback for the response
|
|
55
|
+
* @returns A promise that resolves with the response or rejects with an error
|
|
56
|
+
*/
|
|
35
57
|
private createPromise<TRes, TErr>(c: Req<TRes, TErr>, cbRes?: CbRes<TRes>) {
|
|
36
58
|
return new Promise<TRes>((resolve, reject) => {
|
|
37
59
|
const keySuccess = this.bindPrint(c.notifSuccess.bind);
|
|
@@ -154,10 +176,13 @@ export default class AqmReqs {
|
|
|
154
176
|
if (response?.headers) {
|
|
155
177
|
response.headers.Authorization = '*';
|
|
156
178
|
}
|
|
157
|
-
LoggerProxy.error(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
179
|
+
LoggerProxy.error(
|
|
180
|
+
`Routing request timeout${keySuccess}${JSON.stringify(response)}${c.url}`,
|
|
181
|
+
{
|
|
182
|
+
module: AQM_REQS_FILE,
|
|
183
|
+
method: METHODS.CREATE_PROMISE,
|
|
184
|
+
}
|
|
185
|
+
);
|
|
161
186
|
reject(
|
|
162
187
|
new Err.Details('Service.aqm.reqs.Timeout', {
|
|
163
188
|
key: keySuccess,
|
|
@@ -171,39 +196,60 @@ export default class AqmReqs {
|
|
|
171
196
|
});
|
|
172
197
|
}
|
|
173
198
|
|
|
174
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Converts a bind object to a string representation
|
|
201
|
+
* @param bind - The bind object to convert
|
|
202
|
+
* @returns A string representation of the bind object
|
|
203
|
+
*/
|
|
204
|
+
private bindPrint(bind: any): string {
|
|
175
205
|
let result = '';
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
206
|
+
for (const key of Object.keys(bind).filter((prop) => prop !== '__typeMap')) {
|
|
207
|
+
const value = bind[key];
|
|
208
|
+
|
|
209
|
+
if (Array.isArray(value)) {
|
|
210
|
+
result += `${key}=[${value.join(',')}],`;
|
|
211
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
212
|
+
result += `${key}=(${this.bindPrint(value)}),`;
|
|
182
213
|
} else {
|
|
183
|
-
result += `${
|
|
214
|
+
result += `${key}=${value},`;
|
|
184
215
|
}
|
|
185
216
|
}
|
|
186
217
|
|
|
187
218
|
return result ? result.slice(0, -1) : result;
|
|
188
219
|
}
|
|
189
220
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
221
|
+
/**
|
|
222
|
+
* Checks if a message matches a bind object
|
|
223
|
+
* @param bind - The bind object to check against
|
|
224
|
+
* @param msg - The message to check
|
|
225
|
+
* @returns True if the message matches the bind object, false otherwise
|
|
226
|
+
*/
|
|
227
|
+
private bindCheck(bind: any, msg: any): boolean {
|
|
228
|
+
// Handle type-dependent field matching if __typeMap is present
|
|
229
|
+
if (bind.__typeMap && typeof bind.__typeMap === 'object') {
|
|
230
|
+
if (!AqmReqs.typeMapCheck(bind.__typeMap, msg)) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const key of Object.keys(bind).filter((prop) => prop !== '__typeMap')) {
|
|
236
|
+
const bindValue = bind[key];
|
|
237
|
+
const msgValue = msg[key];
|
|
238
|
+
|
|
239
|
+
if (Array.isArray(bindValue)) {
|
|
194
240
|
// Check if the message value matches any of the values in the array
|
|
195
|
-
if (!
|
|
241
|
+
if (!bindValue.includes(msgValue)) {
|
|
196
242
|
return false;
|
|
197
243
|
}
|
|
198
|
-
} else if (typeof
|
|
199
|
-
if (typeof
|
|
200
|
-
if (!this.bindCheck(
|
|
244
|
+
} else if (typeof bindValue === 'object' && bindValue !== null) {
|
|
245
|
+
if (typeof msgValue === 'object' && msgValue !== null) {
|
|
246
|
+
if (!this.bindCheck(bindValue, msgValue)) {
|
|
201
247
|
return false;
|
|
202
248
|
}
|
|
203
249
|
} else {
|
|
204
250
|
return false;
|
|
205
251
|
}
|
|
206
|
-
} else if (!
|
|
252
|
+
} else if (!msgValue || msgValue !== bindValue) {
|
|
207
253
|
return false;
|
|
208
254
|
}
|
|
209
255
|
}
|
|
@@ -211,7 +257,39 @@ export default class AqmReqs {
|
|
|
211
257
|
return true;
|
|
212
258
|
}
|
|
213
259
|
|
|
214
|
-
|
|
260
|
+
/**
|
|
261
|
+
* Checks type-dependent field conditions defined in __typeMap.
|
|
262
|
+
* @param typeMap - The type map to check against
|
|
263
|
+
* @param msg - The message to check
|
|
264
|
+
* @returns True if the message matches the type map, false otherwise
|
|
265
|
+
* The typeMap has the shape:
|
|
266
|
+
* { typeField: "type", conditions: { EventA: { field: value }, EventB: { field: value } } }
|
|
267
|
+
* It reads msg[typeField] to determine which condition set to apply,
|
|
268
|
+
* then verifies all fields in that condition match the message.
|
|
269
|
+
*/
|
|
270
|
+
private static typeMapCheck(typeMap: any, msg: any): boolean {
|
|
271
|
+
const typeField = typeMap.typeField || 'type';
|
|
272
|
+
const msgType = msg[typeField];
|
|
273
|
+
|
|
274
|
+
if (typeMap.conditions && typeMap.conditions[msgType]) {
|
|
275
|
+
const condition = typeMap.conditions[msgType];
|
|
276
|
+
for (const field of Object.keys(condition)) {
|
|
277
|
+
if (!msg[field] || msg[field] !== condition[field]) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Handles incoming messages from the WebSocket (must be a lambda fn)
|
|
290
|
+
* @param msg - The message to handle
|
|
291
|
+
* @returns
|
|
292
|
+
*/
|
|
215
293
|
private readonly onMessage = (msg: any) => {
|
|
216
294
|
const event = JSON.parse(msg);
|
|
217
295
|
if (event.type === 'Welcome') {
|
|
@@ -48,8 +48,16 @@ export class WebSocketManager extends EventEmitter {
|
|
|
48
48
|
body: SubscribeRequest;
|
|
49
49
|
resource: string;
|
|
50
50
|
}): Promise<WelcomeResponse> {
|
|
51
|
-
const {body, resource} = options;
|
|
52
|
-
|
|
51
|
+
const {body: connectionConfig, resource} = options;
|
|
52
|
+
try {
|
|
53
|
+
await this.register(connectionConfig, resource);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
LoggerProxy.error(`[WebSocketStatus] | Error in registering Websocket ${error}`, {
|
|
56
|
+
module: WEB_SOCKET_MANAGER_FILE,
|
|
57
|
+
method: METHODS.INIT_WEB_SOCKET,
|
|
58
|
+
});
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
53
61
|
|
|
54
62
|
return new Promise((resolve, reject) => {
|
|
55
63
|
this.welcomePromiseResolve = resolve;
|
|
@@ -81,11 +89,23 @@ export class WebSocketManager extends EventEmitter {
|
|
|
81
89
|
|
|
82
90
|
private async register(connectionConfig: SubscribeRequest, resource: string) {
|
|
83
91
|
try {
|
|
92
|
+
// X-ORGANIZATION-ID header is only required for INT environments
|
|
93
|
+
const isIntEnv = this.webex.internal?.services?.isIntegrationEnvironment() || false;
|
|
94
|
+
const orgId = this.webex.credentials.getOrgId();
|
|
95
|
+
|
|
96
|
+
if (isIntEnv && orgId) {
|
|
97
|
+
LoggerProxy.log(`[WebSocketManager] Adding X-ORGANIZATION-ID header for INT environment`, {
|
|
98
|
+
module: WEB_SOCKET_MANAGER_FILE,
|
|
99
|
+
method: METHODS.REGISTER,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
const subscribeResponse: SubscribeResponse = await this.webex.request({
|
|
85
104
|
service: WCC_API_GATEWAY,
|
|
86
105
|
resource,
|
|
87
106
|
method: HTTP_METHODS.POST,
|
|
88
107
|
body: connectionConfig,
|
|
108
|
+
headers: isIntEnv && orgId ? {'X-ORGANIZATION-ID': orgId} : undefined,
|
|
89
109
|
});
|
|
90
110
|
this.url = subscribeResponse.body.webSocketUrl;
|
|
91
111
|
} catch (e) {
|
|
@@ -93,6 +113,7 @@ export class WebSocketManager extends EventEmitter {
|
|
|
93
113
|
`Register API Failed, Request to RoutingNotifs websocket registration API failed ${e}`,
|
|
94
114
|
{module: WEB_SOCKET_MANAGER_FILE, method: METHODS.REGISTER}
|
|
95
115
|
);
|
|
116
|
+
throw e;
|
|
96
117
|
}
|
|
97
118
|
}
|
|
98
119
|
|
package/src/services/index.ts
CHANGED
|
@@ -41,6 +41,7 @@ export default class Services {
|
|
|
41
41
|
constructor(options: {webex: WebexSDK; connectionConfig: SubscribeRequest}) {
|
|
42
42
|
const {webex, connectionConfig} = options;
|
|
43
43
|
this.webSocketManager = new WebSocketManager({webex});
|
|
44
|
+
// TODO: Implement reconnection logic for this websocket in upcoming PR
|
|
44
45
|
this.rtdWebSocketManager = new WebSocketManager({webex});
|
|
45
46
|
const aqmReq = new AqmReqs(this.webSocketManager);
|
|
46
47
|
this.config = new AgentConfigService();
|
|
@@ -414,6 +414,19 @@ export default abstract class Task extends EventEmitter implements ITask {
|
|
|
414
414
|
emitTaskIncoming: this.createEmitSelfAction(TASK_EVENTS.TASK_INCOMING, {
|
|
415
415
|
updateTaskData: true,
|
|
416
416
|
}),
|
|
417
|
+
emitTaskCampaignPreviewReservation: this.createEmitSelfAction(
|
|
418
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
419
|
+
{updateTaskData: true}
|
|
420
|
+
),
|
|
421
|
+
emitTaskCampaignPreviewAcceptFailed: this.createEmitSelfAction(
|
|
422
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED
|
|
423
|
+
),
|
|
424
|
+
emitTaskCampaignPreviewSkipFailed: this.createEmitSelfAction(
|
|
425
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_SKIP_FAILED
|
|
426
|
+
),
|
|
427
|
+
emitTaskCampaignPreviewRemoveFailed: this.createEmitSelfAction(
|
|
428
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED
|
|
429
|
+
),
|
|
417
430
|
emitTaskHydrate: this.createEmitSelfAction(TASK_EVENTS.TASK_HYDRATE, {
|
|
418
431
|
updateTaskData: true,
|
|
419
432
|
}),
|