Skip to content

Commit

Permalink
Merge pull request #486 from techmatters/CHI-2023_warm_transfer_reser…
Browse files Browse the repository at this point in the history
…vation_timeout_stuck_state

Chi 2023 warm transfer reservation timeout stuck state
  • Loading branch information
acedeywin authored Jul 7, 2023
2 parents b46e6a0 + abd5614 commit 713d094
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 26 deletions.
60 changes: 60 additions & 0 deletions functions/conference/getParticipant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* Copyright (C) 2021-2023 Technology Matters
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/

import '@twilio-labs/serverless-runtime-types';
import { Context, ServerlessCallback } from '@twilio-labs/serverless-runtime-types/types';
import {
responseWithCors,
bindResolve,
error400,
error500,
functionValidator as TokenValidator,
success,
} from '@tech-matters/serverless-helpers';

type EnvVars = {
TWILIO_WORKSPACE_SID: string;
};

export type Body = {
callSid: string;
conferenceSid: string;
request: { cookies: {}; headers: {} };
};

export const handler = TokenValidator(
async (context: Context<EnvVars>, event: Body, callback: ServerlessCallback) => {
const response = responseWithCors();
const resolve = bindResolve(callback)(response);

const { callSid, conferenceSid } = event;

try {
if (!callSid) return resolve(error400('callSid'));
if (!conferenceSid) return resolve(error400('conferenceSid'));

const participant = await context
.getTwilioClient()
.conferences(conferenceSid)
.participants(callSid)
.fetch();

return resolve(success({ participant }));
} catch (err: any) {
return resolve(error500(err));
}
},
);
78 changes: 52 additions & 26 deletions functions/taskrouterListeners/transfersListener.private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,53 @@ const isVoiceTransferOriginalInWrapup = (
taskAttributes.transferMeta &&
taskAttributes.transferMeta.transferStatus === 'accepted';

const isWarmVoiceTransferTimedOut = (
eventType: EventType,
taskChannelUniqueName: string,
taskAttributes: { transferMeta?: TransferMeta },
) =>
eventType === RESERVATION_TIMEOUT &&
taskChannelUniqueName === 'voice' &&
taskAttributes.transferMeta &&
taskAttributes.transferMeta.mode === 'WARM';

/**
* updateWarmVoiceTransferAttributes is a DRY function that checks
* when warm voice transfer gets rejected or timeout
*
* If a warm voice transfer gets rejected, it should:
* 1) Adjust original task attributes:
* - transferMeta.transferStatus: 'rejected'
* - transferMeta.sidWithTaskControl: to original reservation
* Same applies to when transfer timeout
*/
const updateWarmVoiceTransferAttributes = async (
transferStatus: string,
context: Context<EnvVars>,
taskAttributes: { transferMeta: { originalReservation: string } },
taskSid: string,
) => {
console.info(`Handling warm voice transfer ${transferStatus} with taskSid ${taskSid}...`);

const client = context.getTwilioClient();

const updatedAttributes = {
...taskAttributes,
transferMeta: {
...taskAttributes.transferMeta,
sidWithTaskControl: taskAttributes.transferMeta.originalReservation,
transferStatus,
},
};

await client.taskrouter
.workspaces(context.TWILIO_WORKSPACE_SID)
.tasks(taskSid)
.update({ attributes: JSON.stringify(updatedAttributes) });

console.info(`Finished handling warm voice transfer ${transferStatus} with taskSid ${taskSid}.`);
};

/**
* Checks the event type to determine if the listener should handle the event or not.
* If it returns true, the taskrouter will invoke this listener.
Expand Down Expand Up @@ -225,34 +272,13 @@ export const handleEvent = async (context: Context<EnvVars>, event: EventFields)
return;
}

/**
* If a warm voice transfer gets rejected, it should:
* 1) Adjust original task attributes:
* - transferMeta.transferStatus: 'rejected'
* - transferMeta.sidWithTaskControl: to original reservation
*/
if (isWarmVoiceTransferRejected(eventType, taskChannelUniqueName, taskAttributes)) {
console.log('Handling warm voice transfer rejected...');

const client = context.getTwilioClient();

const updatedAttributes = {
...taskAttributes,
transferMeta: {
...taskAttributes.transferMeta,
sidWithTaskControl: taskAttributes.transferMeta.originalReservation,
transferStatus: 'rejected',
},
};

await client.taskrouter
.workspaces(context.TWILIO_WORKSPACE_SID)
.tasks(taskSid)
.update({
attributes: JSON.stringify(updatedAttributes),
});
await updateWarmVoiceTransferAttributes('rejected', context, taskAttributes, taskSid);
return;
}

console.log('Finished handling warm voice transfer rejected.');
if (isWarmVoiceTransferTimedOut(eventType, taskChannelUniqueName, taskAttributes)) {
await updateWarmVoiceTransferAttributes('timeout', context, taskAttributes, taskSid);
return;
}

Expand Down

0 comments on commit 713d094

Please sign in to comment.