Skip to content

Commit

Permalink
feat(cc-sdk): added-end-call-edge-case
Browse files Browse the repository at this point in the history
  • Loading branch information
adhmenon committed Dec 6, 2024
1 parent 098948e commit 2f154e3
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 19 deletions.
32 changes: 19 additions & 13 deletions docs/samples/contact-center/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,31 @@ const taskEvents = new CustomEvent('task:incoming', {
},
});

// TODO: Activate the call control buttons once the call is accepted and refctor this
function updateButtonsPostEndCall() {
holdResumeElm.disabled = true;
endElm.disabled = true;
pauseResumeRecordingElm.disabled = true;
wrapupElm.disabled = false;
wrapupCodesDropdownElm.disabled = false;
}

function registerTaskListeners(task) {
task.on('task:assigned', (task) => {
console.info('Call has been accepted for task: ', task.data.interactionId);
holdResumeElm.disabled = false;
holdResumeElm.innerText = 'Hold';
pauseResumeRecordingElm.disabled = false;
pauseResumeRecordingElm.innerText = 'Pause Recording';
endElm.disabled = false;
});
task.on('task:media', (track) => {
document.getElementById('remote-audio').srcObject = new MediaStream([track]);
});
task.on('task:end', (task) => {
// Need to add check here to enable wrapup if the end button is not pressed
console.info('Call ended');
if (!endElm.disabled) {
console.info('Call ended successfully by the external user');
updateButtonsPostEndCall();
}
});
}

Expand Down Expand Up @@ -309,11 +323,6 @@ incomingCallListener.addEventListener('task:incoming', (event) => {
function answer() {
answerElm.disabled = true;
declineElm.disabled = true;
holdResumeElm.disabled = false;
holdResumeElm.innerText = 'Hold';
pauseResumeRecordingElm.disabled = false;
pauseResumeRecordingElm.innerText = 'Pause Recording';
endElm.disabled = false;
task.accept(taskId);
incomingDetailsElm.innerText = 'Call Accepted';
}
Expand Down Expand Up @@ -436,11 +445,8 @@ function togglePauseResumeRecording() {
function endCall() {
endElm.disabled = true;
task.end().then(() => {
console.log('Call ended successfully');
holdResumeElm.disabled = true;
pauseResumeRecordingElm.disabled = true;
wrapupElm.disabled = false;
wrapupCodesDropdownElm.disabled = false;
console.log('Call ended successfully by agent');
updateButtonsPostEndCall();
}).catch((error) => {
console.error('Failed to end the call', error);
endElm.disabled = false;
Expand Down
4 changes: 2 additions & 2 deletions packages/@webex/plugin-cc/src/services/task/TaskManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default class TaskManager extends EventEmitter {
break;
case CC_EVENTS.AGENT_CONTACT_ASSIGNED:
this.currentTask = this.currentTask.updateTaskData(payload.data);
this.emit(TASK_EVENTS.TASK_ASSIGNED, this.currentTask);
this.currentTask.emit(TASK_EVENTS.TASK_ASSIGNED, this.currentTask);
break;
case CC_EVENTS.CONTACT_ENDED:
this.emit(TASK_EVENTS.TASK_UNASSIGNED, this.currentTask);
Expand All @@ -81,7 +81,7 @@ export default class TaskManager extends EventEmitter {
break;
case CC_EVENTS.AGENT_WRAPUP:
this.currentTask = this.currentTask.updateTaskData(payload.data);
this.emit(TASK_EVENTS.TASK_END, this.currentTask);
this.currentTask.emit(TASK_EVENTS.TASK_END, this.currentTask);
break;
default:
break;
Expand Down
14 changes: 14 additions & 0 deletions packages/@webex/plugin-cc/src/services/task/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ export default class Task extends EventEmitter implements ITask {
* */
public async hold(): Promise<TaskResponse> {
try {
if (!this.data.mediaResourceId || this.data.mediaResourceId.length === 0) {
throw new Error('MediaResourceId is required');
}

return this.contact.hold({
interactionId: this.data.interactionId,
data: {mediaResourceId: this.data.mediaResourceId},
Expand All @@ -141,6 +145,10 @@ export default class Task extends EventEmitter implements ITask {
*/
public async resume(): Promise<TaskResponse> {
try {
if (!this.data.mediaResourceId || this.data.mediaResourceId.length === 0) {
throw new Error('MediaResourceId is required');
}

return this.contact.unHold({
interactionId: this.data.interactionId,
data: {mediaResourceId: this.data.mediaResourceId},
Expand Down Expand Up @@ -184,6 +192,12 @@ export default class Task extends EventEmitter implements ITask {
if (!this.data) {
throw new Error('No task data available');
}
if (!wrapupPayload.auxCodeId || wrapupPayload.auxCodeId.length === 0) {
throw new Error('AuxCodeId is required');
}
if (!wrapupPayload.wrapUpReason || wrapupPayload.wrapUpReason.length === 0) {
throw new Error('WrapUpReason is required');
}

return this.contact.wrapup({interactionId: this.data.interactionId, data: wrapupPayload});
} catch (error) {
Expand Down
3 changes: 2 additions & 1 deletion packages/@webex/plugin-cc/src/services/task/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {CallId} from '@webex/calling/dist/types/common/types';
import EventEmitter from 'events';
import {Msg} from '../core/GlobalTypes';

export type TaskId = string;
Expand Down Expand Up @@ -327,7 +328,7 @@ export type TaskResponse = AgentContact | Error | void;
/**
* Represents an interface for managing task related operations.
*/
export interface ITask {
export interface ITask extends EventEmitter {
/**
* Event data received in the CC events
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import config from '../../../../../src/config';
jest.mock('./../../../../../src/services/task', () => {
return jest.fn().mockImplementation(() => {
return {
updateTaskData: jest.fn(),
updateTaskData: jest.fn().mockReturnThis(),
emit: jest.fn()
};
});
});
Expand Down Expand Up @@ -147,11 +148,11 @@ describe('TaskManager', () => {
},
};

const taskAssignedSpy = jest.spyOn(taskManager, 'emit');
const currentTaskAssignedSpy = jest.spyOn(taskManager.currentTask, 'emit');

webSocketManagerMock.emit('message', JSON.stringify(assignedPayload));

expect(taskAssignedSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_ASSIGNED, taskManager.currentTask);
expect(currentTaskAssignedSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_ASSIGNED, taskManager.currentTask);
});

it('should handle WebSocket message for AGENT_CONTACT_RESERVED and emit task:incoming for extension case', () => {
Expand Down
26 changes: 26 additions & 0 deletions packages/@webex/plugin-cc/test/unit/spec/services/task/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ describe('Task', () => {
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'hold', CC_FILE);
});

it('should throw an error if mediaResourceId is missing in hold method', async () => {
task.data.mediaResourceId = '';
await expect(task.hold()).rejects.toThrow();
});

it('should resume the task and return the expected response', async () => {
const expectedResponse: TaskResponse = { data: { interactionId: taskId } } as AgentContact;
contactMock.unHold.mockResolvedValue(expectedResponse);
Expand All @@ -239,6 +244,11 @@ describe('Task', () => {
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'resume', CC_FILE);
});

it('should throw an error if mediaResourceId is missing in hold method', async () => {
task.data.mediaResourceId = '';
await expect(task.resume()).rejects.toThrow();
});

it('should end the task and return the expected response', async () => {
const expectedResponse: TaskResponse = { data: { interactionId: taskId } } as AgentContact;
contactMock.end.mockResolvedValue(expectedResponse);
Expand Down Expand Up @@ -298,6 +308,22 @@ describe('Task', () => {
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'wrapup', CC_FILE);
});

it('should throw an error if auxCodeId is missing in wrapup method', async () => {
const wrapupPayload = {
wrapUpReason: 'Customer request',
auxCodeId: ''
};
await expect(task.wrapup(wrapupPayload)).rejects.toThrow();
});

it('should throw an error if wrapUpReason is missing in wrapup method', async () => {
const wrapupPayload = {
wrapUpReason: '',
auxCodeId: 'auxCodeId123'
};
await expect(task.wrapup(wrapupPayload)).rejects.toThrow();
});

it('should pause the recording of the task', async () => {
await task.pauseRecording();

Expand Down

0 comments on commit 2f154e3

Please sign in to comment.