Alexa: REST API call from intent - node.js

I am trying to code a simple skill. I'm trying to call Rest API from each intent.
For example:
TM.prototype.intentHandlers = {
"startIntent": function (intent, session, response) {
console.log("startIntent start");
// HOW TO CALL get http://mysite.site.com/app/start/1234
console.log("startIntent end");
response.ask("bla bla");
},
"endIntent": function (intent, session, response) {
console.log("endIntent start");
//HOW TO CALL post http://mysite.site.com/app/end/1234
console.log("endIntent end");
response.ask("bla bla bla");
},
Can anyone point me how would I called the URLS. I have try in many ways but the it seems that the request never arrived to the server.
Many thanks, Jeff

Repository of Alexa Cookbooks contains a lot of examples. Performing HTTP calls one of them.
The cookbook describes itself as:
AWS Lambda functions running Node.JS can make calls over the Internet
to APIs and services using the https module included in Javascript.
It contains the example how make HTTP calls.

you can use below sample code to call a REST api,
var req = http.get(url, (res) => {
var body = "";
res.on("data", (chunk) => {
body += chunk
});
res.on("end", () => {
var body = JSON.parse(body);
callBack(body)
});
}).on("error", (error) => {
callBack(err);
});
}
Please don't forgot to add the HTTP package like below,
var http = require('http');

Related

Error: getaddrinfo ENOTFOUND in node.js webhook for dialogflow

I am trying to follow the dialogflow tutorial. I have set up a node.js webhook, that is called from Dialogflow, inside the webhook code I call out to an api. However, my node.js webhook is saying "Error: getaddrinfo ENOTFOUND". This works fine when I run it in visual code but cannot find the api with in the nodejs webhook, when called via DialogFlow. There is something about the fact that it is being called from Dialogflow that seems to be making it not work.
I have spent a lot of time on this, and had previsouly discovered that DialogFlow wont work with https where it is a self signed certificate, as such I put an azure function, so the webhook calls the azure function and then the azure function call the api that I need.
Sorry for the long post...
Here is the node.js code:
'use strict';
const http = require('http');
var request = require('request');
const apiUrl ="https://myapi";
exports.saledurationWebhook = (req, res) => {
// Get the city and date from the request
// let city = req.body.queryResult.parameters['geo-city']; // city is a required param
let city = "sdjk";
// Call the weather API
callSalesDurationApi(city).then((output) => {
res.json({ 'fulfillmentText': output }); // Return the results of the weather API to Dialogflow
})
.catch((err) => {
console.log(err);
//res.json({ 'fulfillmentText': `I don't know the sales duration is but I hope it's quick!` });
res.json({ 'fulfillmentText': err.message});
})
;
};
function callSalesDurationApi(city) {
return new Promise((resolve, reject) => {
console.log('API Request: ' + apiUrl);
var myJSONObject = {
"Inputs": "stuff"
};
request({
url: apiUrl,
method: "POST",
json: true, // <--Very important!!!
body: myJSONObject
}, function (error, response, body) {
console.log("successfully called api");
let output = "Current conditions in the " + body;
console.log(output);
console.log(body);
resolve(output);
});
});
}
Does anyone know why this might be happening? Or what frther steps I can take to investigate it? I have already looked at the loges for the webhook, and for the azure function.
Any help would be really gratefully recieved, I have already wasted days on this. If this is a duplicate question then I am sorry, I have tried to look for existing answers on this issue.
Thanks Laura
I have found this question already answered at: https://stackoverflow.com/a/46692487/7654050
It is because I have not set billing up for this project. I thought it been set up as it is on my work account.

node.js server and AWS asynchronous call issue

I have a simple node Express app that has a service that makesa call to a node server. The node server makes a call to an AWS web service. The AWS simply lists any S3 buckets it's found and is an asynchronous call. The problem is I don't seem to be able to get the server code to "wait" for the AWS call to return with the JSON data and the function returns undefined.
I've read many, many articles on the web about this including promises, wait-for's etc. but I think I'm not understanding the way these work fully!
This is my first exposer to node and I would be grateful if somebody could point me in the right direction?
Here's some snippets of my code...apologies if it's a bit rough but I've chopped and changed things many times over!
Node Express;
var Httpreq = new XMLHttpRequest(); // a new request
Httpreq.open("GET","http://localhost:3000/listbuckets",false);
Httpreq.send(null);
console.log(Httpreq.responseText);
return Httpreq.responseText;
Node Server
app.get('/listbuckets', function (req, res) {
var bucketData = MyFunction(res,req);
console.log("bucketData: " + bucketData);
});
function MyFunction(res, req) {
var mydata;
var params = {};
res.send('Here are some more buckets!');
var request = s3.listBuckets();
// register a callback event handler
request.on('success', function(response) {
// log the successful data response
console.log(response.data);
mydata = response.data;
});
// send the request
request.
on('success', function(response) {
console.log("Success!");
}).
on('error', function(response) {
console.log("Error!");
}).
on('complete', function() {
console.log("Always!");
}).
send();
return mydata;
}
Use the latest Fetch API (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to make HTTP calls. It has built-in support with Promise.
fetch('http://localhost:3000/listbuckets').then(response => {
// do something with the response here
}).catch(error => {
// Error :(
})
I eventually got this working with;
const request = require('request');
request(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
parseString(body, function (err, result) {
console.log(JSON.stringify(result));
});
// from within the callback, write data to response, essentially returning it.
res.send(body);
}
else {
// console.log(JSON.stringify(response));
}
})

Node.js Lambda Function "response is invalid" Amazon Alexa

UPDATE: I had a mistake on my http request endpoint. I had not set the appropriate authentication options so that fixed a lot of errors possibly this specific one.
My question is similar to one here:
Node.js Lambda function returns "The response is invalid" back to Alexa Service Simulator from REST call
However the solution to that question does not solve my problem. So I make an http request call to an xsjs service in Hana cloud. I am getting the 'response is invalid' error message. I can't see why. Here is my function:
// Create a web request and handle the response.
function httpGet(query, callback) {
console.log("/n QUERY: "+ query);
var host = 'datacloudyd070518trial.hanatrial.ondemand.com';
var path = '/LocationInformation/getLocationInfo.xsjs?location=';
var hostname = 'https://' + host + path + query;
var auth = 'user1:D1anafer';
var req = http.request({'hostname': hostname,
'auth': auth
}, (res) => {
var body = '';
res.on('data', (d) => {
body += JSON.stringify(d);
});
res.on('end', function () {
callback(body);
});
});
req.end();
req.on('error', (e) => {
console.error(e);
});
}
And the function that calls it:
'getNewsIntent': function () {
//self = this;
httpGet(location, function (response) {
// Parse the response into a JSON object ready to be formatted.
//var output = JSON.parse(response);
//output = output['change'];
var output = response;
var cardTitle = location;
var cardContent = output;
alexa.emit(':tellWithCard', output, cardTitle, cardContent);
});
},
Thank You
-Diana
Inside your AWS account go to your Lambda function and click on the monitoring tab, where you should see "View Logs in Cloudwatch" in the right hand corner. If you click that link and you should see the errors that are being produced.
You can also use console.log() to log any information being returned from your REST api, which will be logged in cloudwatch and can help you see where your errors are.
This is just a guess from the top of my head. To really help some detailed error message would be required like mentioned about.
But just a guess: Your http.request() is using the http module (https://nodejs.org/api/http.html) and your are accessing the a https resource. If so there is a https (https://nodejs.org/api/https.html) module or use something like axios https://www.npmjs.com/package/axios or requestjs (https://github.com/request/request) this will handle both.
Like I said just a blind guess without detailed error message and seeing your require statements but I am happy to dive deeper if you happen to have details.
HTH
Your callback from the Lambda has to return a valid status code and body. Like this:
let payload = {
statusCode: 400,
body: JSON.stringify('body'),
headers: {"Access-Control-Allow-Origin": "*"}
};
callback(null, payload);
On top of that, to call this from client side code, you have to pass the CORS header back.

Sinon Fake XML Not Capturing Requests

I'm trying to write some tests using Lab and Sinon for various HTTP requests that are called in a file of mine. I followed the Fake XMLHttpRequest example at http://sinonjs.org/ but when I run my tests it appears to not actually capture any requests.
Here is the (relevant) testing code:
context('when provided a valid payload', function () {
let xhr;
let requests;
before(function (done) {
xhr = sinon.useFakeXMLHttpRequest();
requests = [];
xhr.onCreate = function (req) { requests.push(req); };
done();
});
after(function (done) {
// clean up globals
xhr.restore();
done();
});
it('responds with the ticket id', (done) => {
create(internals.validOptions, sinon.spy());
console.log(requests); // Logs empty array []
done();
});
});
create is the function I imported from the other file, here:
internals.create = async function (request, reply) {
const engineeringTicket = request.payload.type === 'engineering';
const urgentTicket = request.payload.urgency === 'urgent';
if (validation.isValid(request.payload)) {
const attachmentPaths = formatUploads(request.payload.attachments);
const ticketData = await getTicket(request.payload, attachmentPaths);
if (engineeringTicket) {
const issueData = getIssue(request.payload);
const response = await jira.createIssue(issueData);
jira.addAttachment(response.id, attachmentPaths);
if (urgentTicket) {
const message = slack.getMessage(response);
slack.postToSlack(message);
}
}
zendesk.submitTicket(ticketData, function (error, statusCode, result) {
if (!error) {
reply(result).code(statusCode);
} else {
console.log(error);
}
});
} else {
reply({ errors: validation.errors }).code(400); // wrap in Boom
}
};
as you can see it calls jira.createIssue and zendesk.submitTicket, both of which use an HTTP request to post some payload to an API. However, after running the test, the requests variable is still empty and seems to have captured no requests. It is definitely not actually submitting the requests as no tickets/issues have been created, what do I need to fix to actually capture the requests?
Your problem is apparent from the tags: you are running the code in NodeJS, but the networking stubs in Sinon is for XMLHttpRequest, which is a browser specific API. It does not exist in Node, and as such, the setup will never work.
That means if this should have worked you would have needed to run the tests in a browser. The Karma test runner can help you with this if you need to automate it.
To make this work in Node you can either go for an approach where you try to stub out at a higher level - meaning stubbing the methods of zendesk and jira, or you can continue with the approach of stubbing network responses (which makes the tests a bit more brittle).
To continue stubbing out HTTP calls, you can do this in Node using Nock. Saving the requests like you did above is done like this:
var requests = [];
var scope = nock('http://www.google.com')
.get('/cat-poems')
.reply(function(uri, requestBody) {
requests.push( {uri, requestBody} );
});
To get some insights on how you can stub out at a higher level, I wrote this answer on using dependency injection and Sinon, while this article by Morgan Roderick gives an intro to link seams.

How to send a HTTP request from Google Cloud Functions (nodeJS)

This is probably a simple question but I'm new to cloud functions/Node programming and haven't found the right documentation yet.
How do I write a Google cloud function that will receive a HTTP request but then send a HTTP request to a different endpoint? For example, I can send the HTTP trigger to my cloud function (https://us-central1-plugin-check-xxxx.cloudfunctions.net/HelloWorldTest). Later in the project I'll figure out how to implement a delay. But then I want to respond with a new HTTP request to a different endpoint (https://maker.ifttt.com/trigger/arrive/with/key/xxxx). How do I do that?
exports.helloWorld = function helloWorld(req, res) {
// Example input: {"message": "Hello!"}
if (req.body.message === undefined) {
// This is an error case, as "message" is required.
res.status(400).send('No message defined!');
} else {
// Everything is okay.
console.log(req.body.message);
res.status(200).send('Success: ' + req.body.message);
// ??? send a HTTP request to IFTTT endpoint here
}
};
Here is the code that I managed to get working with help from Chetan Kanjani. When I send a text message to my Google Cloud function endpoint, it replys with a text message to IFTTT (a different endpoint).
const request = require('request');
exports.helloWorld = function helloWorld(req, res) {
// Example input: {"message": "Hello!"}
if (req.body.message === undefined) {
// This is an error case, as "message" is required.
res.status(400).send('No message defined!');
} else {
// Everything is okay.
console.log(req.body.message);
request.get('https://maker.ifttt.com/trigger/arrival/with/key/xxxx', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); //Prints the response of the request.
});
res.status(200).send("Success");
}
};
I also had to change the package.json file to include the request package. It already had the sample-http package, I added the dependencies:
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"request": "^2.81.0"
}
}
I'm still not sure where the console.log function prints out the information. That might be helpful for future debugging.
The Request module uses callbacks. If you want to use JavaScript promises instead, the Axios module provides equivalent functionality.
Old, but I came across this while searching myself:
request module with promise support is (request-promise)
The code below worked. Not sure if Axios is the ideal module for simple requests like these, but the Google Cloud Function documentation uses Axios so it seemed sensible to also use Axios. Other answers use the request module, but it was deprecated in February 2020.
Note: GCF doesn't support ES6 natively at this time. ES6 support is coming with Node 13.
Package.json
{
"name": "YOUR_NAME",
"version": "0.0.1",
"dependencies": {
"axios": "^0.19.2"
}
}
Index.js
/**
* Required Modules
*/
const axios = require("axios");
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.run = async(req, res) => {
// Set API end point.
let apiURL = YOUR_URL;
// Wrap API parameters in convenient object.
let apiData = {
PARAM_1: PARAM_DATA,
PARAM_2: PARAM_DATA
};
// Invoke API.
axios.post(apiURL,
JSON.stringify(apiData)
)
.then((response) => {
res.status(200).send(response.data);
console.log(response);
}, (error) => {
res.status(500).send(response.data);
console.log(error);
});
};
Use https://www.npmjs.com/package/request module.
var request = require('request');
request.get('https://maker.ifttt.com/trigger/arrive/with/key/xxxx', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); //Prints the response of the request.
});

Resources