Unable to send xml response with serverless framework - twilio

I'm working with twilio in which when call comes to my twilio number it invokes webhook, I'm using lambda function as webhook,
twilio expects xml(formerly called twiml) response from webhook and i'm unable to send xml response from lambda function
I'm using serverless framework
here is my code
function:
module.exports.voice = (event, context, callback) => {
console.log("event", JSON.stringify(event))
var twiml = new VoiceResponse();
twiml.say({ voice: 'alice' }, 'Hello, What type of podcast would you like to listen? ');
twiml.say({ voice: 'alice' }, 'Please record your response after the beep. Press any key to finish.');
twiml.record({
transcribe: true,
transcribeCallback: '/voice/transcribe',
maxLength: 10
});
console.log("xml: ", twiml.toString())
context.succeed({
body: twiml.toString()
});
};
yml:
service: aws-nodejs
provider:
name: aws
runtime: nodejs6.10
timeout: 10
iamRoleStatements:
- Effect: "Allow"
Action: "*"
Resource: "*"
functions:
voice:
handler: handler.voice
events:
- http:
path: voice
method: post
integration: lambda
response:
headers:
Content-Type: "'application/xml'"
template: $input.path("$")
statusCodes:
200:
pattern: '.*' # JSON response
template:
application/xml: $input.path("$.body") # XML return object
headers:
Content-Type: "'application/xml'"
Response:
please let me know if I'm making some mistake in code
also created an issue on github
Thanks,
Inzamam Malik

You don't need to mess with serverless.yml so much. Here is the simple way:
In serverless.yml...
functions:
voice:
handler: handler.voice
events:
- http:
path: voice
method: post
(response, headers, Content-Type, template, and statusCodes are not necessary)
Then you can just set the statusCode and Content-Type in your function.
So delete this part...
context.succeed({
body: twiml.toString()
});
... and replace it with:
const response = {
statusCode: 200,
headers: {
'Content-Type': 'text/xml',
},
body: twiml.toString(),
};
callback(null, response);
Lambda proxy integration (which is the default) assembles it into a proper response.
Personally I find this way simpler and more readable.

you need your lambda to be a "proxy" type, so you set the body property.
but just try to do
context.succeed(twiml.toString());
that will send the "string" as result directly
or use the callback param:
function(event, context, callback) {
callback(null, twiml.toString())
}

As mentioned by #UXDart, you'll not be able to do this using the standard integration. You should setup a proxy integration with Lambda like here -http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-proxy-integration-lambda-function-nodejs
This will work better with what you are trying to do, return xml through api gateway.

Change your serverless.yml to this:
service: aws-nodejs
provider:
name: aws
runtime: nodejs6.10
timeout: 10
iamRoleStatements:
- Effect: "Allow"
Action: "*"
Resource: "*"
functions:
voice:
handler: handler.voice
events:
- http:
path: voice
method: post
integration: lambda
response:
headers:
Content-Type: "'application/xml'"
template: $input.path("$")
statusCodes:
200:
pattern: '' # Default response method
template:
# Your script returns json, so match it here
application/json: $input.path("$.body")
headers:
Content-Type: "'application/xml'"

Got mine to work with this.
events:
- http:
path: call/receive
method: post
integration: lambda
response:
headers:
Content-Type: "'application/xml'"
template: $input.path("$")
statusCodes:
200:
pattern: ''
template:
application/json: $input.path("$")
headers:
Content-Type: "'application/xml'"
and
callback(null, twiml.toString());

It works for me.
webhook:
handler: webhook.webhook
events:
- http:
path: webhook
method: get
cors: true
integration: lambda
response:
headers:
Content-Type: "'application/xml'"
template: $input.path("$")
statusCodes:
200:
pattern: '' # Default response method
template:
# Your script returns json, so match it here
application/json: $input.path("$.body")
headers:
Content-Type: "'application/xml'"

Checkout the twilio serverless example here: https://github.com/serverless/examples/tree/master/aws-node-twilio-send-text-message

Related

Zapier API Configuration: send form-data instead of json request body

I am setting up a Zap for our application in Zapier.
However, I've run into some trouble having the Zap pass over the data in the correct format.
By default it appears Zapier passes the data as json request body, but our backend only accepts form-data.
Is it possible to configure the Zap to send over form-data instead?
In the code below, I've tried to send the data as both params and body, but my backend doesn't any of it as form-data:
const options = {
url: '${URL}',
method: 'POST',
headers: {
'Authorization': ${token},
'Content-Type': 'application/json',
'Accept': 'application/json'
},
params: {
'phone': bundle.inputData.phone,
'email': bundle.inputData.email,
'dialog': bundle.inputData.dialog,
'name': bundle.inputData.name
},
body: {
'name': bundle.inputData.name,
'email': bundle.inputData.email,
'phone': bundle.inputData.phone,
'dialog': bundle.inputData.dialog
}
}
return z.request(options)
.then((response) => {
response.throwForStatus();
const results = z.JSON.parse(response.content);
// You can do any parsing you need for results here before returning them
return results;
});
Any input is greatly appreciated!
I fixed it by replacing 'Content-Type': 'application/json' with 'Content-Type': 'application/x-www-form-urlencoded'.

Angular 8 httpclient response header empty for POST method

I have enabled Access-Control-Expose-Headers from my backend. But still in angular 8 http client POST method call , response headers are empty.
it always shows
HttpResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "http://localhost:8080/rest/auth/login", ok: true, …}
headers: HttpHeaders
normalizedNames: Map(0)
[[Entries]]
No properties
size: (...)
__proto__: Map
lazyUpdate: null
lazyInit: () => {…}
__proto__: Object
status: 200
statusText: "OK"
url: "http://localhost:8080/rest/auth/login"
ok: true
In your Angular app where you're sending the request, pass an object {observe: 'response'}.
http
.post<any>('url', {observe: 'response'})
.subscribe(response => {
console.log(response.headers.get('x-auth-token'));
});

POST to VoilaNorbert API from Rails with HTTParty

I am trying to POST data to the standard VoilaNorbert search API endpoint using the HTTParty Rails gem:
response = HTTParty.post(
'https://api.voilanorbert.com/2018-01-08/search/name',
query: {
'name': 'Elon Musk',
'domain': 'https://www.tesla.com/'
},
headers: {
'Content-Type': 'application/json',
'Authorization': VOILANORBERT_API_TOKEN
}
)
However, when I try this simple request, I get the response:
=> {"name"=>["This field is required."]}
My understanding is that the query block above is supposed to pass along the name field, which in this case is "Elon Musk".
What am I missing here?
After some trial and error, I succeeded by realizing I needed to change the payload to send form-data in the body of the request rather than as query params. This requires using the body block to encode form fields, as well as the multipart method on the request indicating that data is being uploaded.
response = HTTParty.post(
'https://api.voilanorbert.com/2018-01-08/search/name',
body: {
name: 'Elon',
domain: 'https://www.tesla.com/'
},
multipart: true,
headers: {
'Content-Type': 'application/json',
'Authorization': VOILANORBERT_API_TOKEN
}
)

Twilio Forward SMS to email - Cannot find module 'got'

I'm new to Twilio. I'm attempting to forward an SMS to an email address using this tutorial:
https://www.twilio.com/blog/2017/07/forward-incoming-sms-messages-to-email-with-node-js-sendgrid-and-twilio-functions.html
I feel certain I've done everything it says to do, but I get an error 11200 HTTP retrieval failure every time, with these details:
{
"message": "Cannot find module 'got'",
"name": "Error",
"stack": "Error: Cannot find module 'got'\n at Function.Module._resolveFilename (module.js:547:15)\n at
Function.Module._load (module.js:474:25)\n at Module.require
(module.js:596:17)\n at Module.twilioRequire [as require]
(/var/task/node_modules/enigma-lambda/src/dependency.js:28:21)\n at
require (internal/module.js:11:18)\n at Object.
(/var/task/handlers/ZFa37cc3db9fd8db0501c2e5fc92137969.js:1:75)\n
at Module._compile (module.js:652:30)\n at
Object.Module._extensions..js (module.js:663:10)\n at Module.load
(module.js:565:32)\n at tryModuleLoad (module.js:505:12)" }
I've tried making absolutely sure I have the function written the same as the tutorial. I copied it directly from the github page to be sure. I'm not sure how to proceed in troubleshooting this, it seems it's telling me that 'got' isn't found but it's supposed to be available in Twilio functions. Any ideas? Thanks.
Edit: Here is the code:
const got = require('got');
exports.handler = function(context, event, callback) {
const requestBody = {
personalizations: [{ to: [{ email: context.TO_EMAIL_ADDRESS }] }],
from: { email: context.FROM_EMAIL_ADDRESS },
subject: `New SMS message from: ${event.From}`,
content: [
{
type: 'text/plain',
value: event.Body
}
]
};
got
.post('https://api.sendgrid.com/v3/mail/send', {
headers: {
Authorization: `Bearer ${context.SENDGRID_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
})
.then(response => {
let twiml = new Twilio.twiml.MessagingResponse();
callback(null, twiml);
})
.catch(err => {
callback(err);
});
};
After a little more research, I found some comments on GitHub that indicate 'got' is no longer included by default in the Twilio dependencies. Per the instructions there I went to the Runtime Functions Config section of the Twilio console and added got version 6.7.1 and now the original source code works!
I prefer Alex's solution however since it works "out of the box" and I'm keeping it as the accepted answer.
First, the above code with got works with my Twilio and SendGrid accounts, I just tested, I don't know why you're having trouble..., maybe try to create a Twilio subaccount and run from there.
Second, if you still can't get got to work, here is some code, you could try,
and I'we also tested and it works. It's using https instead:
const https = require('https');
exports.handler = function (context, event, callback) {
let postData = JSON.stringify({
personalizations: [{
to: [{
email: 'somebody#gmail.com'
}]
}],
from: {
email: 'somebody#gmail.com'
},
subject: `New SMS message from: ${event.From}`,
content: [{
type: 'text/plain',
value: event.Body
}]
});
let postOptions = {
host: 'api.sendgrid.com',
port: '443',
path: '/v3/mail/send',
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData),
}
};
let req = https.request(postOptions, function (res) {
// some code to handle the async response if needed
let twiml = new Twilio.twiml.MessagingResponse();
callback(null, twiml);
});
req.write(postData);
req.end();
};
Good luck!
I was able to get this to work by making sure that "got" is installed as a dependency under these settings here:
https://www.twilio.com/console/runtime/functions/configure
Functions Screenshot

Nock how to mock put request

My real codes like:
fetch(hostname+ '/api/save', {
method: "put",
credentials: "include",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({a: 'b'})
})
nock like:
nock(hostname)
.put('/api/save')
.reply(200);
the hostname includes port, but it's random port in the test. I've checked the log, the real url and test url are same. But I don't know why it doesn't match???

Resources