Skip to content

Commit

Permalink
Merge pull request #664 from techmatters/CHI-2818-fix_transfer_target
Browse files Browse the repository at this point in the history
CHI-2818: Change capacity adjustment approach
  • Loading branch information
stephenhand authored Aug 15, 2024
2 parents dd5c11d + 79cadc4 commit 3eb0ea8
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 50 deletions.
14 changes: 13 additions & 1 deletion functions/adjustChatCapacity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ type EnvVars = {

export type Body = {
workerSid?: string;
adjustment?: 'increase' | 'decrease' | 'increaseUntilCapacityAvailable';
// 'increase' and 'decease' can be removed once backend manual pulling is enabled everywhere
adjustment?: 'increase' | 'decrease' | 'increaseUntilCapacityAvailable' | 'setTo1';
request: { cookies: {}; headers: {} };
};

Expand Down Expand Up @@ -120,6 +121,17 @@ export const adjustChatCapacity = async (
return { status: 200, message: 'Successfully decreased channel capacity' };
}

if (body.adjustment === 'setTo1') {
if (channel.configuredCapacity !== 1) {
await channel.update({ capacity: 1 });
// If configuredCapacity is already 1, send status 200 to avoid error on client side
return { status: 200, message: 'Successfully reset channel capacity to 1' };
}

// If configuredCapacity is already 1, send status 200 to avoid error on client side
return { status: 200, message: 'Channel capacity already 1, no adjustment made.' };
}

return { status: 400, message: 'Invalid adjustment argument' };
};

Expand Down
2 changes: 1 addition & 1 deletion functions/pullTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const handler = TokenValidator(
}
}
}
await adjustChatCapacity(context, { workerSid, adjustment: 'decrease' });
await adjustChatCapacity(context, { workerSid, adjustment: 'setTo1' });
resolve(send(404)({ message: 'No task found to pull' }));
return undefined;
},
Expand Down
15 changes: 11 additions & 4 deletions functions/taskrouterListeners/adjustCapacityListener.private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
import { Context } from '@twilio-labs/serverless-runtime-types/types';
import {
EventType,
TASK_COMPLETED,
RESERVATION_ACCEPTED,
RESERVATION_REJECTED,
EventFields,
TaskrouterListener,
} from '@tech-matters/serverless-helpers/taskrouter';
import type { AdjustChatCapacityType } from '../adjustChatCapacity';

export const eventTypes: EventType[] = [TASK_COMPLETED];
export const eventTypes: EventType[] = [RESERVATION_ACCEPTED, RESERVATION_REJECTED];

type EnvVars = {
TWILIO_WORKSPACE_SID: string;
Expand All @@ -36,6 +37,13 @@ type EnvVars = {
*/
export const shouldHandle = (event: EventFields) => eventTypes.includes(event.EventType);

/**
* After a reservation is accepted, we reset the capacity to 1
* We only need to increase capacity temporarily to accept an additional task under certain circumstances like transfers and manual task pulls
* So since for 'regular' auto assigned tasks resetting to 1 is a noop, we can just always reset to 1 when any reservation is accepted / rejected
* @param context
* @param event
*/
export const handleEvent = async (context: Context<EnvVars>, event: EventFields) => {
try {
const {
Expand All @@ -58,10 +66,9 @@ export const handleEvent = async (context: Context<EnvVars>, event: EventFields)

// eslint-disable-next-line global-require,import/no-dynamic-require,prefer-destructuring
const adjustChatCapacity: AdjustChatCapacityType = require(path).adjustChatCapacity;

const body = {
workerSid,
adjustment: 'decrease',
adjustment: 'setTo1',
} as const;

await adjustChatCapacity(context, body);
Expand Down
38 changes: 0 additions & 38 deletions functions/taskrouterListeners/transfersListener.private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import {
} from '@tech-matters/serverless-helpers/taskrouter';
import type { TransferMeta, ChatTransferTaskAttributes } from '../transfer/helpers.private';
import { InteractionChannelParticipants } from '../interaction/interactionChannelParticipants.private';
import { AdjustChatCapacityType } from '../adjustChatCapacity';

export const eventTypes: EventType[] = [
RESERVATION_ACCEPTED,
Expand Down Expand Up @@ -156,37 +155,6 @@ const updateWarmVoiceTransferAttributes = async (
*/
export const shouldHandle = (event: EventFields) => eventTypes.includes(event.EventType);

const decreaseChatCapacity = async (context: Context<EnvVars>, taskSid: string) => {
const serviceConfig = await context.getTwilioClient().flexApi.configuration.get().fetch();
const {
feature_flags: { enable_backend_manual_pulling: enableBackendManualPulling },
} = serviceConfig.attributes;
if (enableBackendManualPulling) {
const task = await context
.getTwilioClient()
.taskrouter.workspaces(context.TWILIO_WORKSPACE_SID)
.tasks(taskSid);
const reservations = await task.reservations.list();
const workerSid = reservations.find((r) => r.reservationStatus === 'accepted')?.workerSid;

if (!workerSid) {
console.warn(`No worker found for task ${taskSid} to decrease chat capacity.`);
return;
}

const { path } = Runtime.getFunctions().adjustChatCapacity;
// eslint-disable-next-line global-require,import/no-dynamic-require,prefer-destructuring
const adjustChatCapacity: AdjustChatCapacityType = require(path).adjustChatCapacity;

const body = {
workerSid,
adjustment: 'decrease',
} as const;

await adjustChatCapacity(context, body);
}
};

export const handleEvent = async (context: Context<EnvVars>, event: EventFields) => {
try {
const {
Expand All @@ -212,9 +180,6 @@ export const handleEvent = async (context: Context<EnvVars>, event: EventFields)

const { originalTask: originalTaskSid } = taskAttributes.transferMeta;

// We need to decrease chat capacity before completing the task, it until the task completed event introduces a race condition
// The worker can still be offered another task before capacity is reduced if we don't do it now
await decreaseChatCapacity(context, originalTaskSid);
const client = context.getTwilioClient();

await client.taskrouter.workspaces
Expand Down Expand Up @@ -254,9 +219,6 @@ export const handleEvent = async (context: Context<EnvVars>, event: EventFields)

const { originalTask: originalTaskSid } = taskAttributes.transferMeta;

// We need to decrease chat capacity before completing the task, it until the task completed event introduces a race condition
// The worker can still be offered another task before capacity is reduced if we don't do it now
await decreaseChatCapacity(context, originalTaskSid);
const client = context.getTwilioClient();

await client.taskrouter.workspaces
Expand Down
40 changes: 34 additions & 6 deletions functions/transferChatStart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ async function increaseChatCapacity(

const body = {
workerSid: worker?.sid as string,
adjustment: 'increase',
adjustment: 'increaseUntilCapacityAvailable',
} as const;

await adjustChatCapacity(context, body);
Expand All @@ -167,6 +167,7 @@ async function increaseChatCapacity(

export const handler = TokenValidator(
async (context: Context<EnvVars>, event: Body, callback: ServerlessCallback) => {
console.info('===== transferChatStart invocation =====');
const client = context.getTwilioClient();

const response = responseWithCors();
Expand Down Expand Up @@ -197,7 +198,7 @@ export const handler = TokenValidator(
.workspaces(context.TWILIO_WORKSPACE_SID)
.tasks(taskSid)
.fetch();

console.debug('Original task fetched', originalTask);
const originalAttributes = JSON.parse(originalTask.attributes);

const transferTargetType = targetSid.startsWith('WK') ? 'worker' : 'queue';
Expand Down Expand Up @@ -265,6 +266,9 @@ export const handler = TokenValidator(

let newTaskSid;
if (isConversation && transferTargetType === 'worker') {
console.info(
`Transferring conversations task ${taskSid} to worker ${targetSid} by creating interaction invite.`,
);
// Get task queue
const taskQueues = await client.taskrouter
.workspaces(context.TWILIO_WORKSPACE_SID)
Expand All @@ -274,8 +278,8 @@ export const handler = TokenValidator(

// Create invite to target worker
const invite = await client.flexApi.v1.interaction
.get(originalAttributes.flexInteractionSid)
.channels.get(originalAttributes.flexInteractionChannelSid)
.get(flexInteractionSid)
.channels.get(flexInteractionChannelSid)
.invites.create({
routing: {
properties: {
Expand All @@ -290,10 +294,31 @@ export const handler = TokenValidator(
});

newTaskSid = invite.routing.properties.sid;
console.info(
`Transferred conversations task ${taskSid} to worker ${targetSid} by creating interaction invite.`,
);
} else if (isConversation && transferTargetType === 'queue') {
console.info(
`Transferring conversations task ${taskSid} to queue ${targetSid} by creating interaction invite.`,
);
Object.entries({
flexInteractionSid,
flexInteractionChannelSid,
TWILIO_CONVERSATIONS_CHAT_TRANSFER_WORKFLOW_SID:
context.TWILIO_CONVERSATIONS_CHAT_TRANSFER_WORKFLOW_SID,
TWILIO_WORKSPACE_SID: context.TWILIO_WORKSPACE_SID,
newAttributes,
taskChannelUniqueName: originalTask.taskChannelUniqueName,
}).forEach(([key, value]) => {
console.debug(`${key}:`, value);
});
console.debug('newAttributes:');
Object.entries(newAttributes).forEach(([key, value]) => {
console.debug(`${key}:`, value);
});
const invite = await client.flexApi.v1.interaction
.get(originalAttributes.flexInteractionSid)
.channels.get(originalAttributes.flexInteractionChannelSid)
.get(flexInteractionSid)
.channels.get(flexInteractionChannelSid)
.invites.create({
routing: {
properties: {
Expand All @@ -305,6 +330,9 @@ export const handler = TokenValidator(
},
});

console.info(
`Transferred conversations task ${taskSid} to queue ${targetSid} by creating interaction invite.`,
);
newTaskSid = invite.routing.properties.sid;
} else {
// Edit channel attributes so that original task won't cause issues with the transferred one
Expand Down

0 comments on commit 3eb0ea8

Please sign in to comment.