Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/@webex/contact-center/src/cc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -704,12 +704,38 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
module: CC_FILE,
method: METHODS.CONNECT_WEBSOCKET,
});

// Track successful Mercury connection
this.metricsManager.trackEvent(
METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_SUCCESS,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an open question - should we track mercury metrics along with the other regular websocket metric events?
Or should we have a separate metric for this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i thought this is specifc to the contact center socket and has nothing to do with mercury, calling one is different not sure how they would work here

{
connectionType: 'mercury',
agentId: this.agentConfig?.agentId,
},
['operational']
);
})
.catch((error) => {
LoggerProxy.error(`Error occurred during mercury.connect() ${error}`, {
module: CC_FILE,
method: METHODS.CONNECT_WEBSOCKET,
error,
});

// Track Mercury connection failure
this.metricsManager.trackEvent(
METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
{
connectionType: 'mercury',
error: error?.toString() || 'Mercury connection failed',
errorMessage: error?.message || 'Unknown error',
agentId: this.agentConfig?.agentId,
},
['operational']
);

// Note: Not throwing here as Mercury connection failure shouldn't block
// the overall registration, but it will prevent WebRTC calling
});
}
if (this.$config && this.$config.allowAutomatedRelogin) {
Expand Down
1 change: 1 addition & 0 deletions packages/@webex/contact-center/src/metrics/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const METRIC_EVENT_NAMES = {
FETCH_BUDDY_AGENTS_FAILED: 'Fetch Buddy Agents Failed',
WEBSOCKET_REGISTER_SUCCESS: 'Websocket Register Success',
WEBSOCKET_REGISTER_FAILED: 'Websocket Register Failed',
WEBSOCKET_CONNECTION_ERROR: 'Websocket Connection Error',
AGENT_RONA: 'Agent RONA',
AGENT_CONTACT_ASSIGN_FAILED: 'Agent Contact Assign Failed',
AGENT_INVITE_FAILED: 'Agent Invite Failed',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import LoggerProxy from '../../../logger-proxy';
import workerScript from './keepalive.worker';
import {KEEPALIVE_WORKER_INTERVAL, CLOSE_SOCKET_TIMEOUT, METHODS} from '../constants';
import {WEB_SOCKET_MANAGER_FILE} from '../../../constants';
import MetricsManager from '../../../metrics/MetricsManager';
import {METRIC_EVENT_NAMES} from '../../../metrics/constants';

/**
* WebSocketManager handles the WebSocket connection for Contact Center operations.
Expand Down Expand Up @@ -137,6 +139,19 @@ export class WebSocketManager extends EventEmitter {
`[WebSocketStatus] | event=socketConnectionFailed | WebSocket connection failed ${event}`,
{module: WEB_SOCKET_MANAGER_FILE, method: METHODS.CONNECT}
);

// Track WebSocket connection error in metrics
MetricsManager.getInstance({webex: this.webex}).trackEvent(
METRIC_EVENT_NAMES.WEBSOCKET_CONNECTION_ERROR,
{
error: event?.toString() || 'WebSocket connection error',
errorType: event?.type || 'unknown',
connectionUrl: this.url || 'unknown',
onlineStatus: navigator.onLine,
},
['operational']
);

reject();
};

Expand Down
38 changes: 31 additions & 7 deletions packages/@webex/contact-center/src/services/task/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,33 @@ export default class Task extends EventEmitter implements ITask {
method: METHODS.SETUP_AUTO_WRAPUP_TIMER,
interactionId: this.data.interactionId,
});
await this.wrapup({
wrapUpReason: defaultWrapupReason.name,
auxCodeId: defaultWrapupReason.id,
});
try {
await this.wrapup({
wrapUpReason: defaultWrapupReason.name,
auxCodeId: defaultWrapupReason.id,
});
} catch (error) {
// Track auto-wrapup failure metrics
this.metricsManager.trackEvent(
METRIC_EVENT_NAMES.TASK_WRAPUP_FAILED,
{
taskId: this.data?.interactionId,
wrapUpCode: defaultWrapupReason.id,
wrapUpReason: defaultWrapupReason.name,
isAutoWrapup: true,
error: error.toString(),
errorMessage: error?.message || 'Auto-wrapup failed',
},
['operational', 'behavioral', 'business']
);

LoggerProxy.error(`Auto-wrapup failed: ${error}`, {
module: TASK_FILE,
method: METHODS.SETUP_AUTO_WRAPUP_TIMER,
interactionId: this.data?.interactionId,
error,
});
}
});
}
}
Expand Down Expand Up @@ -394,7 +417,7 @@ export default class Task extends EventEmitter implements ITask {
taskId: this.data.interactionId,
error: error.toString(),
...taskErrorProps,
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details as Failure),
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error?.details as Failure),
},
['operational', 'behavioral', 'business']
);
Expand Down Expand Up @@ -438,6 +461,7 @@ export default class Task extends EventEmitter implements ITask {
return Promise.resolve();
} catch (error) {
const err = generateTaskErrorObject(error, METHODS.TOGGLE_MUTE, TASK_FILE);

throw err;
}
}
Expand Down Expand Up @@ -496,10 +520,10 @@ export default class Task extends EventEmitter implements ITask {
this.metricsManager.trackEvent(
METRIC_EVENT_NAMES.TASK_DECLINE_FAILED,
{
taskId: this.data.interactionId,
taskId: this.data?.interactionId,
error: error.toString(),
...taskErrorProps,
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details || {}),
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error?.details || {}),
},
['operational', 'behavioral']
);
Expand Down
1 change: 1 addition & 0 deletions packages/@webex/contact-center/test/unit/spec/cc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ describe('webex.cc', () => {
{
module: CC_FILE,
method: 'connectWebsocket',
error: mockError,
}
);
expect(connectWebsocketSpy).toHaveBeenCalled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ describe('WebSocketManager', () => {

mockWebex = {
request: jest.fn(),
once: jest.fn(),
ready: false,
} as unknown as WebexSDK;

mockWorker = {
Expand Down Expand Up @@ -234,6 +236,51 @@ describe('WebSocketManager', () => {
);
});

it('should track metrics on WebSocket error event', async () => {
const subscribeResponse = {
body: {
webSocketUrl: 'wss://fake-url',
},
};

(mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);

// Mock MetricsManager
const mockMetricsManager = {
trackEvent: jest.fn(),
};

const MetricsManagerModule = require('../../../../../../src/metrics/MetricsManager');
jest.spyOn(MetricsManagerModule.default, 'getInstance').mockReturnValue(mockMetricsManager);

// Mock navigator.onLine to be true for this test
Object.defineProperty(global, 'navigator', {
value: {
onLine: true,
},
configurable: true,
});

await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });

const errorEvent = new Event('error');
Object.defineProperty(errorEvent, 'type', { value: 'error', writable: false });

MockWebSocket.inst.onerror(errorEvent);

// Verify metrics tracking was called
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
'Websocket Connection Error',
{
error: '[object Event]',
errorType: 'error',
connectionUrl: 'wss://fake-url',
onlineStatus: true,
},
['operational']
);
});

it('should handle WebSocket message event with AGENT_MULTI_LOGIN', async () => {
const subscribeResponse = {
body: {
Expand Down Expand Up @@ -371,4 +418,4 @@ describe('WebSocketManager', () => {
{ module: WEB_SOCKET_MANAGER_FILE, method: 'webSocketOnCloseHandler' }
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,7 @@ describe('Task', () => {
interactionId: task.data.interactionId,
});
});


describe('AutoWrapup initialization tests', () => {
beforeEach(() => {
Expand Down
Loading