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

Echo Funlet #18

Closed
eric-brechemier opened this issue Jul 11, 2019 · 12 comments
Closed

Echo Funlet #18

eric-brechemier opened this issue Jul 11, 2019 · 12 comments

Comments

@eric-brechemier
Copy link
Contributor

Migrate the Echo Twimlet to an equivalent Twilio Function Template.

Stage 1

Respond with the contents of the Twiml parameter.

@eric-brechemier
Copy link
Contributor Author

eric-brechemier commented Jul 12, 2019

Annotated Source Code

based on a snapshot of Echo TwiML source code (echo.php)
sent to me by Twilio support by email on 2019-07-08

This is PHP code.

<?php

It is shared by Twilio under the MIT license.

/*
Copyright (c) 2012 Twilio, Inc.

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/

Send Content-Type header for an XML document (TwiML is XML).

// send XML header
header('Content-type: text/xml');

Send the contents of the Twiml parameter as response.

// echo the TwiML passed into the URL
echo $_GET['Twiml'];

(there is no check that we received valid TwiML at this point, invalid TwiML will fail further in the flow)

@eric-brechemier
Copy link
Contributor Author

eric-brechemier commented Jul 12, 2019

Test Case

[Echo-1] Successful Echo

Input

Parameter Value
Twiml <Response><Say>echo okay</Say></Response>

Output

$ curl -s https://twimlets.com/echo?Twiml=%3CResponse%3E%3CSay%3Eecho%20okay%3C%2FSay%3E%3C%2FResponse%3E \
| xmllint --format -
<?xml version="1.0"?>
<Response>
  <Say>echo okay</Say>
</Response>

@eric-brechemier
Copy link
Contributor Author

Implementation

I created a first version of the Echo Funlet.

I started by developing it online by creating a new Twilio Function in my Twilio account:

exports.handler = function(context, event, callback) {
  // echo the TwiML passed into the `Twiml` property/parameter
  callback(null, event.Twiml || "<response/>" );
};

This implementation works but does not set the Content-Type to text/xml:

content-type: text/plain; charset=utf8

I found in the documentation how to construct a response with custom headers, using an instance of Twilio.Response:

exports.handler = function(context, event, callback) {
  let response = new Twilio.Response();
  response.appendHeader("Content-Type", "text/xml");
  // echo the TwiML passed into the `Twiml` property/parameter
  response.setBody( event.Twiml || "<response/>" );
  callback(null, response);
};

This works and returns the expected Content Type:

content-type: text/xml; charset=utf8

But when trying to do the same, including unit tests, in the environment of this project, I managed to define the first implementation, but the second one, using Twilio.Response fails:

TypeError: Twilio.Response is not a constructor

       7 | 
       8 |   // Create a custom response in XML format
    >  9 |   let response = new Twilio.Response();

@dkundel is there something that I should be aware of?

@philnash
Copy link
Contributor

I don’t think this is something I implemented for the custom test harness, which you can see here: https://github.com/twilio-labs/function-templates/blob/master/test/test-helper.js. Will have to look into this for the more complicated responses like this (ironic for such a simple twimlet!). You can probably set the tests as pending for now if you want to carry on with implementation.

@dkundel
Copy link
Member

dkundel commented Jul 19, 2019

Yeah this seems to be a test issue. I think we should be using some parts of twilio-run for the tests actually @philnash. I was thinking about that the other day but haven't had time to work on it. I'll file an issue for this.

But yeah for your case you'd be fine with your implementation and set the testing to pending for now. The only nitpick is that the default should be <Response /> with a capital R

@eric-brechemier
Copy link
Contributor Author

You can probably set the tests as pending for now if you want to carry on with implementation.

I'll do that. Thanks.

@eric-brechemier
Copy link
Contributor Author

eric-brechemier commented Jul 25, 2019

Unit Tests

 PASS  funlet-echo/funlet-echo.test.js
  ✓ [ECHO-INPUT-TWIML-1] Read Twiml from Event (3ms)
  ✓ [ECHO-INPUT-TWIML-2] Read Twiml from Context
  ✓ [ECHO-INPUT-TWIML-3] Read Default Twiml from Script
  ✓ [ECHO-OUTPUT-ECHO-1] echo() shall return its input (1ms)
  ○ skipped 1 test

OK for now: the skipped test is [ECHO-1] Full Response, which depends on the missing Twilio.Response to run.

@eric-brechemier
Copy link
Contributor Author

eric-brechemier commented Jul 25, 2019

Integration Tests

After deploying the funlet to my Twilio account, with the Check for valid Twilio signature unchecked, and setting DOMAIN to my runtime domain, running this script allows to check that we get the same output with the Funlet as with the Twimlet:

$ ./test-echo.sh "https://$DOMAIN.twil.io/echo"
[Echo-1] Successful Echo
<?xml version="1.0"?>
<Response>
  <Say>echo okay</Say>
</Response>

The comparison is not actually done in the script, but can be done as a second step, in case of doubt:

$ ./test-echo.sh https://twimlets.com/echo > /tmp/echo.twimlet.txt
$ ./test-echo.sh "https://$DOMAIN.twil.io/echo" > /tmp/echo.funlet.txt
$ diff /tmp/echo.twimlet.txt /tmp/echo.funlet.txt

@dkundel
Copy link
Member

dkundel commented Jul 26, 2019

Ooooooh I love that 😊

@eric-brechemier
Copy link
Contributor Author

I don’t think this is something I implemented for the custom test harness, which you can see here: https://github.com/twilio-labs/function-templates/blob/master/test/test-helper.js.

@philnash I was not using the test helper. I have added it to the unit tests for the Echo Funlet, and with minimal modifications of my tests, it works fine for my purpose.

In retrospect, my confusion stems from the fact that I expected the Twilio object to be the Twilio Node Helper library imported using require(). I have now noticed that it is actually a special global object set up by the Twilio runtime environment, and none of the other Twilio Function templates import it.

Is there a GitHub project where I can find the definition of this special global Twilio object used in Twilio Functions?

@philnash
Copy link
Contributor

The Twilio object is the Node helper library with a Response object, that is only used in Functions, also defined. twilio-run defines that here and this is the implementation of Response,and the public definition is in the docs here.

@eric-brechemier
Copy link
Contributor Author

Thanks! I see the light 💡!

@eric-brechemier eric-brechemier mentioned this issue Aug 29, 2019
1 task
@dkundel dkundel closed this as completed in 2c61019 Sep 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants