Skip to content

Commit

Permalink
Merge pull request #696 from techmatters/CHI-2966-fix_convo_listener
Browse files Browse the repository at this point in the history
CHI-2966 fix convo listener
  • Loading branch information
stephenhand authored Oct 3, 2024
2 parents ce859b3 + cb3e1b7 commit cbf3b27
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 85 deletions.
16 changes: 11 additions & 5 deletions functions/helpers/sendErrorMessageForUnsupportedMedia.private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ import { Context } from '@twilio-labs/serverless-runtime-types/types';

export type ConversationSid = `CH${string}`;

type SendErrorMessageForUnsupportedMediaEvent = {
type OnMessageAddedEvent = {
EventType: 'onMessageAdded';
Body?: string;
ConversationSid: ConversationSid;
EventType?: string;
Media?: Record<string, any>;
DateCreated: Date;
};

export type Event = SendErrorMessageForUnsupportedMediaEvent;
export type Event = OnMessageAddedEvent;

const FALLBACK_ERROR_MESSAGE = 'Unsupported message type.';
const ERROR_MESSAGE_TRANSLATION_KEY = 'UnsupportedMediaErrorMsg';
Expand Down Expand Up @@ -56,28 +56,33 @@ export const sendConversationMessage = async (
});

export const sendErrorMessageForUnsupportedMedia = async (context: Context, event: Event) => {
const { EventType, Body, Media, ConversationSid } = event;
const { Body, Media, ConversationSid } = event;

/* Valid message will have either a body/media. A message with no
body or media implies that there was an error sending such message
*/
if (EventType === 'onMessageAdded' && !Body && !Media) {
if (!Body && !Media) {
console.debug('Message has no text body or media, sending error.', ConversationSid);
let messageText = FALLBACK_ERROR_MESSAGE;

const serviceConfig = await context.getTwilioClient().flexApi.configuration.get().fetch();
const helplineLanguage = serviceConfig.attributes.helplineLanguage ?? 'en-US';

console.debug('Helpline language to send error message: ', helplineLanguage, ConversationSid);
if (helplineLanguage) {
try {
const response = await fetch(
`https://${context.DOMAIN_NAME}/translations/${helplineLanguage}/messages.json`,
);
const translation = await response.json();
const { [ERROR_MESSAGE_TRANSLATION_KEY]: translatedMessage } = translation;

console.debug('Translated error message: ', translatedMessage, ConversationSid);
messageText = translatedMessage || messageText;
} catch {
console.warn(
`Couldn't retrieve ${ERROR_MESSAGE_TRANSLATION_KEY} message translation for ${helplineLanguage}`,
ConversationSid,
);
}
}
Expand All @@ -87,6 +92,7 @@ export const sendErrorMessageForUnsupportedMedia = async (context: Context, even
author: 'Bot',
messageText,
});
console.info('Sent error message: ', messageText, ConversationSid);
}
};

Expand Down
25 changes: 15 additions & 10 deletions functions/webhooks/serviceConversationListener.protected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,29 @@
*/

import { Context, ServerlessCallback } from '@twilio-labs/serverless-runtime-types/types';
import { responseWithCors, bindResolve, error500 } from '@tech-matters/serverless-helpers';
import { responseWithCors, bindResolve, error500, success } from '@tech-matters/serverless-helpers';
import {
Event,
SendErrorMessageForUnsupportedMedia,
} from '../helpers/sendErrorMessageForUnsupportedMedia.private';

export const handler = async (context: Context, event: Event, callback: ServerlessCallback) => {
console.info(`===== Service Conversation Listener (event: ${event.EventType})=====`);
const response = responseWithCors();
const resolve = bindResolve(callback)(response);
// eslint-disable-next-line global-require,import/no-dynamic-require
const sendErrorMessageForUnsupportedMedia = require(Runtime.getFunctions()[
'helpers/sendErrorMessageForUnsupportedMedia'
].path).sendErrorMessageForUnsupportedMedia as SendErrorMessageForUnsupportedMedia;
if (event.EventType === 'onMessageAdded') {
// eslint-disable-next-line global-require,import/no-dynamic-require
const sendErrorMessageForUnsupportedMedia = require(Runtime.getFunctions()[
'helpers/sendErrorMessageForUnsupportedMedia'
].path).sendErrorMessageForUnsupportedMedia as SendErrorMessageForUnsupportedMedia;

try {
await sendErrorMessageForUnsupportedMedia(context, event);
} catch (err) {
if (err instanceof Error) resolve(error500(err));
else resolve(error500(new Error(String(err))));
try {
console.debug('New message, checking if we need to send error.');
await sendErrorMessageForUnsupportedMedia(context, event);
} catch (err) {
if (err instanceof Error) return resolve(error500(err));
return resolve(error500(new Error(String(err))));
}
}
return resolve(success(event));
};
72 changes: 2 additions & 70 deletions tests/webhooks/serviceConversationListener.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,77 +49,9 @@ describe('serviceConversationListener', () => {
helpers.teardown();
});

test('Should return status 400 if Body, Media or EventType are undefined', async () => {
const event1: Body = {
Body: undefined,
ConversationSid: 'CHxxxxxxx34EWS',
EventType: 'onMessageAdded',
Media: {},
DateCreated: new Date(),
};

const event2: Body = {
Body: 'Test word',
ConversationSid: 'CHxxxxxxx34EWS',
EventType: 'onMessageAdded',
Media: undefined,
DateCreated: new Date(),
};

const event3: Body = {
Body: 'Test word',
ConversationSid: 'CHxxxxxxx34EWS',
EventType: undefined,
Media: {},
DateCreated: new Date(),
};

const callback: ServerlessCallback = (err, result) => {
expect(result).toBeDefined();
const response = result as MockedResponse;
expect(response.getStatus()).toBe(400);
};

await serviceConversationListener(baseContext, event1, callback);
await serviceConversationListener(baseContext, event2, callback);
await serviceConversationListener(baseContext, event3, callback);
});

test('Should return status 500 if object response is undefined', async () => {
const event1: Body = {
Body: 'Test word',
ConversationSid: 'CHxxxxxxx34EWS',
EventType: 'onMessageAdded',
Media: {},
DateCreated: new Date(),
};

const callback1: ServerlessCallback = (err, result) => {
expect(result).toBeDefined();
const response = result as MockedResponse;
expect(response.getStatus()).toBe(500);
};

await serviceConversationListener({ ...baseContext }, event1, callback1);

const event2: Body = {
Body: 'Test word',
ConversationSid: 'CHxxxxxxx34EWS',
EventType: 'onMessageAdded',
Media: {},
DateCreated: new Date(),
};

const callback2: ServerlessCallback = (err, result) => {
expect(result).toBeDefined();
const response = result as MockedResponse;
expect(response.getStatus()).toBe(500);
};

await serviceConversationListener(baseContext, event2, callback2);
});
// TODO: Test coverage for sending error message

test('Should return status 200', async () => {
test('Should return status 200 for valid message', async () => {
const event1: Body = {
Body: 'Test word',
ConversationSid: 'CHxxxxxxx34EWS',
Expand Down

0 comments on commit cbf3b27

Please sign in to comment.