Twilio Functions Utils
ABOUT
![Coveralls](https://img.shields.io/coveralls/github/iagocalazans/twilio-functions-utils?color=white&logo=coveralls&style=for-the-badge)
This lib was created with the aim of simplifying the use of serverless Twilio, reducing the need to apply frequent try-catches and improving context management, making it no longer necessary to return the callback() method in all functions.
Install
npm install twilio-functions-utils
HOW IT WORKS
The lib provides a function useInjection
who returns a brand function for every execution. This returned function is ready to receive the Twilio Handler arguments and make them available as this
properties as this.request
, this.cookies
and this.env
at the Function level and this.client
and this.env
at the Provider function level.
# useInjection(Function, Options) Function
The useInjection method takes two parameters. The first to apply as a handler and the last is an object of configuration options.
[useInjection] Function Function
Must be writen in standard format, this will be your handler
function.
function createSomeThing (event) {
...
}
[useInjection] Options.providers Object
An object that can contain providers that will be defined, which act as use cases to perform internal actions in the handler function through the this.providers
method.
[useInjection] Options.validateToken Boolean
You can pass validateToken
equal true to force Flex Token validation using Twilio Flex Token Validator
useInjection(yourFunction,
{
providers: { create, remove },
validateToken: true
}
);
When using Token Validator, the Request body must contain a valid Token from Twilio Flex.
{
Token: "Twilio-Token-Here"
}
Response Class
The responses coming from the function destined to the handler must be returned as an instance of Response.
Response receives a string and a number (status code):
return new Response('Your pretty answer.', 200);
There are two failure response models, BadRequest and NotFound. Its use follows the same model.
const notFound = new NotFoundError('Your error message here.');
const badRequest = new BadRequestError('Your error message here.');
TwiMLResponse Class
There is a proper response template to use with the TwiML format:
const twimlVoice = new Twilio.twiml
.VoiceResponse();
const enqueueVoice = twimlVoice
.enqueue({
action,
workflowSid,
})
.task('{}');
return new TwiMLResponse(twimlVoice, 201)
Usage
IMPORTANT TO USE REGULAR FUNCTIONS ➜ With arrow functions it doesn't work as expected as this
cannot be injected correctly.
function yourFunctionName() {
}
Separate your actions from the main routine of the code. Break it down into several smaller parts that interact with your event, to facilitate future changes. You can create functions such as Assets or Functions, then just import them through the Runtime and pass them to the provider.
const { Try } = require('twilio-functions-utils');
exports.create = async function (event) {
const { client, env } = this
return Try.promise(new Promise((resolve, reject) => {
const random = Math.random();
if (random >= 0.5) {
return resolve({ sucess: 'Resolved' });
}
return reject(new Error('Unresolved'));
}));
};
In your handler you will have access to the function through the providers property, internal to the this of the function that precedes the handler.
const { useInjection, Response } = require('twilio-functions-utils');
const { create } = require(Runtime.getAssets()['/create.js'].path)
async function createAction(event) {
const { cookies, request, env } = this
const createTry = await this.providers.create(event)
if (createTry.isError) {
return new BadRequestError(createTry.error);
}
return new Response(createTry.data, 201);
}
exports.handler = useInjection(createAction, {
providers: {
create,
},
validateToken: true,
});
# typeOf(Value) Function
A simple method to discovery a value type. This is more specific then the original JavaScript typeof
.
It will return as Array
, Object
, String
, Number
, Symbol
.
[typeOf] Value *
Could be any JavaScript primitive value to be type checked.
Usage
const { typeOf } = require('twilio-functions-utils');
const type = typeOf('my name is Lorem');
const typeArray = typeOf(['one', 'two']);
const original = typeof ['one', 'two']
console.log(type)
console.log(typeArray)
console.log(original)
# Try Static
The Try class provides an organized and simple way to return errors without having to wrap every request in Try Catches.
Methods
Try.promise(data)
Use the .promise
method to create a new Try instance with a promised data property and isError false
.
[Try.promise] data Promise<*>
The data value could be of any of the primitives types that javascript accpets incapsulated by a Promise.
Properties
Try.isError
A boolean propety that return true when Try contain a defined error value.
Try.data
The successfully returned value.
Try.error
An Error like object throwed by the "action" as Try.
Usage
const result = Try.promise(value);
const result = Try.success(value);
const result = Try.failed(error);
if (result.isError) {
return new BadRequestError(result.error)
}
return new Response(result.data)
TESTING
# useMock(Function, Options) Function
The Twilio Serverless structure make it hard for testing sometimes. So this provides a method that works perfectly with useInjection ready functions. The useMock
act like useInjection but mocking some required fragments as getAssets
and getFunctions
.
[useMock] Function Function
The same function as used in useInjection
.
[useMock] Options.providers Object
Unlike useInjection
, the useMock
method only receives the Options.providers
property.
Usage
(Required) Set your jest
testing script with NODE_ENV=test
:
"scripts": {
"test": "NODE_ENV=test jest --collect-coverage --watchAll",
"start": "twilio-run",
"deploy": "twilio-run deploy"
}
Your files structures must be have assets
and functions
into first or second levels starting from src
(when in second level):
app/
├─ package.json
├─ node_modules/
├─ src/
│ ├─ functions/
│ ├─ assets/
or:
app/
├─ package.json
├─ functions/
├─ assets/
├─ node_modules/
Exports your function to be tested and your handler so it can be used by Twilio when in runtime:
async function functionToBeTested(event) {
const something = await this.providers.myCustomProvider(event)
return Response(something)
}
const handler = useInjection(functionToBeTested, {
providers: {
myCustomProvider,
},
});
module.exports = { functionToBeTested, handler };
(Required) You always need to import the twilio.mock
for Response Twilio Global object on your testing files begining.
require('twilio-functions-utils/lib/twilio.mock');
Use Twilio Functions Utils useMock
to do the hard job and just write your tests with the generated function.
You can use Twilio.mockRequestResolvedValue
, Twilio.mockRequestImplementation
, Twilio.mockRequestRejectedValue
to Mock your Twilio API requests.
require('twilio-functions-utils/lib/twilio.mock');
const { useMock, Response } = require('twilio-functions-utils');
const { functionToBeTested } = require('../../functions/functionToBeTested');
const fn = useMock(functionToBeTested, {
providers: {
myCustomProvider: async (sid) => ({ sid }),
},
env: {
YOUR_ENV_VAR: 'value'
},
client: {
functionToMock: {}
}
});
describe('Function functionToBeTested', () => {
it('if {"someValue": true}', async () => {
const request = { TaskSid: '1234567', TaskAttributes: '{"someValue": true}' };
Twilio.mockRequestResolvedValue({
statusCode: 200,
body: {
sid: '1234567'
}
})
Twilio.mockRequestResolvedValue({
statusCode: 200,
body: {
key: "MP****",
data: { sid: '7654321' }
}
})
const res = await fn(request);
const customMap = await Runtime.getSync().maps("MP****").fetch();
expect(res).toBeInstanceOf(Response);
expect(res.body).not.toEqual(request);
expect(res.data).toEqual({ sid: '7654321' });
expect(res.body).toEqual({ sid: '1234567' });
});
});
AUTHOR