Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Studio flow creation proof of concept #398

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 134 additions & 106 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
"voice-javascript-sdk",
"voicemail",
"verify-sna",
"flex-dialpad"
"flex-dialpad",
"studio-quick-deploy-test"
]
}
9 changes: 9 additions & 0 deletions studio-quick-deploy-test/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# description: Your Twilio phone number for sending and receiving messages
# format: phone_number
# required: true
TWILIO_PHONE_NUMBER=

# description: SID of the Vaccine Intake Studio Flow
# configurable: false
# format: sid
FLOW_SID=
2 changes: 2 additions & 0 deletions studio-quick-deploy-test/.owners
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bherman
# Insert your Github username here
8 changes: 8 additions & 0 deletions studio-quick-deploy-test/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog

## [Unreleased]

## [1.0.0]
### Added
- Initial release.

64 changes: 64 additions & 0 deletions studio-quick-deploy-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# studio-quick-deploy-test

Test try out studio deploy test

## Pre-requisites

### Environment variables

This project requires some environment variables to be set. A file named `.env` is used to store the values for those environment variables. To keep your tokens and secrets secure, make sure to not commit the `.env` file in git. When setting up the project with `twilio serverless:init ...` the Twilio CLI will create a `.gitignore` file that excludes `.env` from the version history.

In your `.env` file, set the following values:

| Variable | Description | Required |
| :------- | :---------- | :------- |


### Function Parameters

`/blank` expects the following parameters:

| Parameter | Description | Required |
| :-------- | :---------- | :------- |


`/hello-messaging` is protected and requires a valid Twilio signature as well as the following parameters:

| Parameter | Description | Required |
| :-------- | :---------- | :------- |


## Create a new project with the template

1. Install the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart#install-twilio-cli)
2. Install the [serverless toolkit](https://www.twilio.com/docs/labs/serverless-toolkit/getting-started)

```shell
twilio plugins:install @twilio-labs/plugin-serverless
```

3. Initiate a new project

```
twilio serverless:init example --template=studio-quick-deploy-test && cd example
```

4. Start the server with the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart):

```
twilio serverless:start
```

5. Open the web page at https://localhost:3000/index.html and enter your phone number to test

ℹ️ Check the developer console and terminal for any errors, make sure you've set your environment variables.

## Deploying

Deploy your functions and assets with either of the following commands. Note: you must run these commands from inside your project folder. [More details in the docs.](https://www.twilio.com/docs/labs/serverless-toolkit)

With the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart):

```
twilio serverless:deploy
```
57 changes: 57 additions & 0 deletions studio-quick-deploy-test/assets/studio_flow.private.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable camelcase */
const flowDefinition = {
description: 'A New Flow',
flags: {
allow_concurrent_calls: true,
},
status: 'published',
timeout: 3600,
initial_state: 'Trigger',
states: [
{
type: 'trigger',
name: 'Trigger',
properties: {
offset: {
x: 380,
y: 70,
},
},
transitions: [
{
event: 'incomingMessage',
next: 'send_message',
},
{
event: 'incomingCall',
},
{
event: 'incomingRequest',
},
],
},
{
type: 'send-message',
name: 'send_message',
properties: {
offset: {
x: 400,
y: 240,
},
body: 'Thank you for your interest in this property! The open house is on Saturday from 1pm - 6pm.',
from: '{{flow.channel.address}}',
to: '{{contact.channel.address}}',
},
transitions: [
{
event: 'sent',
},
{
event: 'failed',
},
],
},
],
};

module.exports = flowDefinition;
17 changes: 17 additions & 0 deletions studio-quick-deploy-test/functions/delete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
exports.handler = async function (context, event, callback) {
const client = context.getTwilioClient();

console.log(`Deleting ${context.SERVICE_SID}`);
return client.serverless.v1
.services(context.SERVICE_SID)
.remove()
.then((res) => {
console.log(res);
callback();
})
.catch((err) => {
console.error(err);
console.error(err.details);
callback();
});
};
110 changes: 110 additions & 0 deletions studio-quick-deploy-test/functions/index.html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
const helpers = require('@twilio-labs/runtime-helpers');
const axios = require('axios');

/* eslint-disable no-console, func-names */
exports.handler = async function (context, event, callback) {
const assets = Runtime.getAssets();
const flowDefinition = require(assets['/studio_flow.js'].path);

const client = context.getTwilioClient();
const currentEnvironment = await helpers.environment.getCurrentEnvironment(
context
);

function getStudioRedirectUrl(flowSid) {
return `https://console.twilio.com?frameUrl=%2Fconsole%2Fstudio%2Fflows%2F${flowSid}`;
}

// TODO: Move to it's own function which can be kicked off async?
async function cleanupService() {
console.log(`Deleting - https://${context.DOMAIN_NAME}/delete`);
return axios.get(`https://${context.DOMAIN_NAME}/delete`);
}

// Deploy Twilio Studio Flow
function deployStudio() {
return client.studio.v2.flows
.create({
commitMessage: 'Code Exchange automatic deploy',
friendlyName: 'Quick Deploy Test',
status: 'published',
definition: flowDefinition,
})
.then((flow) => flow)
.catch((err) => {
console.error(err);
console.error(err.details);
throw new Error(err.details);
});
}

function getPhoneNumberSid() {
return new Promise((resolve, reject) => {
client.incomingPhoneNumbers
.list({ phoneNumber: context.TWILIO_PHONE_NUMBER, limit: 20 })
.then((incomingPhoneNumbers) => {
const n = incomingPhoneNumbers[0];
resolve(n.sid);
})
.catch((err) => reject(err));
});
}

function updatePhoneNumberWebhook(studioWebhook, numberSid) {
return new Promise((resolve, reject) => {
client
.incomingPhoneNumbers(numberSid)
.update({
smsUrl: studioWebhook,
})
.then(() => {
resolve('success');
})
.catch((err) => reject(err));
});
}

function setFlowSidEnvVar(environment, sid) {
return helpers.environment.setEnvironmentVariable(
context,
environment,
'FLOW_SID',
sid
);
}

// Create redirect response to Studio
const response = new Twilio.Response();
response.setStatusCode(308); // Perm redirect

// IF FLOW_SID ALREADY EXISTS THE FLOW HAS BEEN CREATED. SHOT CIRCUIT, PREVENTING MULTIPLE FLOWS
const flowSid = await helpers.environment.getEnvironmentVariable(
context,
currentEnvironment,
'FLOW_SID'
);
if (flowSid) {
console.log('FLOW ALREADY CREATED');
response.appendHeader('Location', getStudioRedirectUrl(flowSid));
await cleanupService();

return callback(null, response);
}

console.log('CREATING NEW FLOW');
const flow = await deployStudio();

// No environment exists when developing locally
if (currentEnvironment) {
await setFlowSidEnvVar(currentEnvironment, flow.sid);
} else {
process.env.FLOW_SID = flow.sid;
}

const phoneNumberSid = await getPhoneNumberSid();
await updatePhoneNumberWebhook(flow.webhookUrl, phoneNumberSid);

response.appendHeader('Location', getStudioRedirectUrl(flow.sid));
await cleanupService();
return callback(null, response);
};
11 changes: 11 additions & 0 deletions studio-quick-deploy-test/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "studio-quick-deploy-test",
"version": "1.0.0",
"private": true,
"dependencies": {
"@twilio-labs/runtime-helpers": "^0.1.2",
"@twilio-labs/serverless-runtime-types": "^2.2.3",
"axios": "^1.3.0",
"twilio": "^3.84.1"
}
}
20 changes: 20 additions & 0 deletions studio-quick-deploy-test/tests/delete.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const helpers = require('../../test/test-helper');
const deleteFunction = require('../functions/delete');
const Twilio = require('twilio');

const context = {};
const event = {};

beforeAll(() => {
helpers.setup(context);
});

afterAll(() => {
helpers.teardown();
});

describe('studio-quick-deploy-test auth function', () => {
it('Does something', () => {
console.log('Something');
});
});
7 changes: 7 additions & 0 deletions studio-quick-deploy-test/tests/index.test.html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { handler } = require('../functions/index.html');

describe('studio-quick-deploy-test function template', () => {
it('Calls callback with empty JSON', () => {
console.log('fix this');
});
});
5 changes: 5 additions & 0 deletions templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,11 @@
"id": "experimental-flex-dialpad",
"name": "Native Flex Dialpad Add-on",
"description": "Adds agent to agent outbound call and external number transfer to Twilio Flex native dialpad "
},
{
"id": "studio-quick-deploy-test",
"name": "Studio Quick Deploy Test",
"description": "Test try out studio deploy test"
}
]
}
Loading