I am making a skill for the Amazon Echo. In my handlers, I have an intent (SelectGardenIntent) that obtains the user_id (needed for following HTTP requests) from the access token successfully, as well as a variable called gardenNumber which is a slot value. To complete the request, I need two other values, the garden_id and the dev_id. I use this gardenNumber and pass it into a function called getGardenId, which will assign the one of the data from the HTTP request to the variable garden_id I have defined in index.js. There are no issues with user_id and gardenNumber. When the function is run, there are no errors from the request, but the callback function with the response is also not executed. The user_id, "about to enter request", and "req done" are correctly logged when tested, but the other log statements in the callback function are not since it is not run. The result is that garden_id is undefined. dev_id is obtained in another method that depends on this garden_id, so dev_id is also undefined. Please help me on this issue. I have pasted the relevant code below.
...
var user_id, garden_id, dev_id;
...
function getGardenId (gardenNumber) {
console.log(user_id);
var path = '/api/rest/client/getgardeninfo?&userid=' + user_id;
var options = {
hostname: server_ip,
port: 80,
path: path,
method: 'GET'
}
console.log("about to enter request");
var req = http.request(options, (res) => {
console.log('entered request');
if (res.statusCode === 200) {
console.log('successful request');
res.setEncoding('utf8');
var body = "";
res.on('data', (chunk) => {
console.log('adding data');
body += chunk.toString();
});
res.on('end', () => {
var obj = JSON.parse(body);
console.log('successfully parsed');
if (obj.error === 200) {
console.log('##gardenid successfully obtained');
garden_id = obj.data[gardenNumber - 1].id;
} else {
console.log("parsing error");
}
});
} else {
console.log("failed request");
}
}); } catch(e) {
console.log("ERROR");
}
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.on('finish', () => {
console.log('ended');
})
req.end();
console.log("req done");
}
...
var handlers = {
...
'SelectGardenIntent': function () {
//var filledSlots = delegateSlotCollection.call(this);
var gardenNumber = this.event.request.intent.slots.Garden.value;
user_id = this.event.session.user.accessToken;
getGardenId(gardenNumber);
getDevId(garden_id);
this.emit(':tell', `OK, garden ${gardenNumber} selected, user id is ${user_id}, garden id is ${garden_id}, device id is ${dev_id}`);
}
...
}
You'd better use npm request to make calls.
request.get({
url: 'http://' + server_ip + '/api/rest/client/getgardeninfo?&userid=' + user_id
}, function (err, res, body) {
console.log(body);
})
Related
My res.json in my first block of code works, but in the else part of my if statement, it does not. The block that doesnt work, checks for a record in a database then im trying to return the response but im not receiving it.
I've checked and the response is a string, I thought it would have worked as the top part of the code successfully returns the string and it shows in dialogflow (where im trying to return it)
The response is successfully consoled right before the res.json but I do not receive it from the source of the request.
code:
app.post('/webhook/orderinfo', (req, res) => {
const intent = req.body.queryResult.intent.displayName;
const domain = "chatbotdemo.myshopify.com";
const order = req.body.queryResult.parameters["number-sequence"];
if (intent.includes('Order Number')) {
url = "https://test-hchat.com/api/orders/" + domain + "/" + order;
request(url)
.then(function (response) {
order_res = JSON.parse(response)
order_res["fullfillmentText"] = "Hi, Please find your order details below:";
res.json({
"fulfillmentText": JSON.stringify(order_res)
})
})
.catch(function (err) {
console.log(err)
});
// THIS PART DOESNT RETURN THE RESPONSE.
} else {
const domain = 'testStore'
db.getClientsDialog(domain, intent, (response) => {
const fullResponse = response.response
res.json({
fullResponse
})
})
}
});
The database code:
getClientsDialog: function (domain, intent, callback) {
MongoClient.connect('mongodb://efwefewf#wefwef.mlab.com:15799/wefwef', function (err, client) {
if (err) throw err;
var db = client.db('asdsad');
db.collection('dialog').findOne({ domain: domain, intent: intent }, function (err, doc) {
if (!err) {
callback(doc)
} else {
throw err;
callback(err)
}
client.close();
});
console.dir("Called findOne");
});
}
Could it be because this second use of the res.json in the else statement, is trying to call the db first and therefore the link is lost to send the data back?
So I have a POST route that calls a function:
router.route('/generateSeed').post(function(req,res){
generate_seed(res)
});
UPDATE: Here is the genrate_seed() function
function generate_seed(res)
{
var new_seed = lightwallet.keystore.generateRandomSeed();
generate_addresses(new_seed, res);
}
var totalAddresses = 0;
function generate_addresses(seed, res)
{
if(seed == undefined)
{
console.log("seed")
}
var password = Math.random().toString();
lightwallet.keystore.createVault({
password: password,
seedPhrase: seed,
hdPathString: "m/44'/60'/0'/0" //added this changing from Default m/0'/0'/0'/
}, function (err, ks) {
ks.keyFromPassword(password, function (err, pwDerivedKey) {
if(err)
{
}
else
{
ks.generateNewAddress(pwDerivedKey, totalAddresses);
var addresses = ks.getAddresses()
var web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io"));//changed to infura as a provider
var html = "";
var address = addresses;
var seedPhrase = seed;
addToAPI(address,seedPhrase, res); //address
}
});
});
}
function addToAPI(address, seedPhrase, res){
var NewUser = {
publicK: address,
seed: seedPhrase
}
axios.post('http://localhost:3000/CryptoWallet/add/createCryptoWallet', NewUser)//changed from localhost
.then((res)=>{
console.log("Response");
})
.catch(error=>{
console.log(error);
})
}
Which calls to this second route:
router.route('/add/createCryptoWallet').post(function(req,res){
var crypto_wallet = new CryptoWallet(req.body)
console.log("The cyrptoWallet on create", crypto_wallet);
crypto_wallet.save()
.then(crypto_wallet =>{
res.json({data: CryptoWallet({_id:1})}); //<<--- I want this line
})
.catch(err => {
res.status(400).send("unable to save CryptoWallet to databse");
});
});
UPDATE I do get it to POST and save in the database. Right now I can only get the response from the first POST route is there a way to get the response from the second POST route my final goal is the get the _id created by mongo as a response.
Thanks ahead!
You are missing response sending for your first POST request (/generateSeed). addToAPI function need to wait until second POST request is finished and the send its own response. So basically it should look similar to this:
function addToAPI(address, seedPhrase, res){
var NewUser = {
publicK: address,
seed: seedPhrase
}
axios.post('http://localhost:3000/CryptoWallet/add/createCryptoWallet', NewUser)
.then((response)=>{
res.json(response.data); // axios wrappes the body of response into `data` object
})
.catch(error=>{
console.log(error);
res.status(500).('Some error occured');
})
}
I am working on an Express.js app. The current feature is creating an appointment with a post request and getting and saving data from third party API, then sending updated API data in the subsequent request. The feature is fully working but in the test, the function to get API data is not getting called.
Route to create appointment:
app.post('/schedule', requestHandler.postSchedule);
The request handler for creating appointment:
requestHandler.postSchedule = function (req, res) {
let appointment = {
// posted data
};
new Appointment(appointment)
.save()
.then(newAppointment => {
if(req.body.cityName && req.body.cityName !== '') {
console.log('req.body.cityName', req.body.cityName);
weatherHelper.addNewCityWeatherData(req.body.cityName);
}
return newAppointment;
})
.then(newAppointment => {
// do some other stuff
res.send(newAppointment);
})
.catch(err => {
error(err);
});
};
Function to add weather data:
exports.addNewCityWeatherData = (city) => {
console.log('City in addNewCityWeatherData', city);
getCurrentTrackingCities(cities => {
if(cities.indexOf(city) < 0) {
console.log(city + ' data not in weather');
getWeatherData(city, data => {
console.log('Got weather data');
addWeatherDataToDB(city, data);
});
} else {
console.log('City exists');
}
});
};
Function to get weather data from API:
getWeatherData = (city, callback) => {
console.log('getWeatherData called', city);
let url = `http://api.apixu.com/v1/forecast.json?key=${weatherApiKey}&q=${city}&days=${10}`
request(url, (err, res, body) => {
console.log('Weather data received body');
callback(body);
});
};
When testing, this feature fails and all console logs are printed except the 'Weather data received body' and the logs in consequent functions.
Here is my test:
describe.only('Weather data', function() {
let requestWithSession = request.defaults({jar: true});
let hashedPass = bcrypt.hashSync('testpass', null);
beforeEach((done) => {
new User({
'name': 'Test User',
'email': 'testuser#test.com',
'password': hashedPass
})
.save()
.then(() => {
let options = {
'method': 'POST',
'uri': testHost + '/login',
'form': {
'email': 'testuser#test.com',
'password': 'testpass'
}
};
requestWithSession(options, (err, res, body) => {
done();
});
});
}); // beforeEach
afterEach((done) => {
// remove test stuff from db
}); // afterEach
it('Adds weather data when an appointment with new city is posted', (done) => {
let options = {
'method': 'POST',
'uri': testHost + '/schedule',
'form': {
'title': 'Test title',
'description': 'Test description',
'start_date_time': '2017-07-19 01:00',
'end_date_time': '2017-07-19 02:00',
'cityName': 'New York',
'isTrackingWeather': 'true'
}
};
// post request to add appointment data
requestWithSession(options, (err, res, body) => {
if(err) {
console.log('DatabaseError in Weather Data');
throw {
type: 'DatabaseError',
message: 'Failed to create test setup data'
};
}
let options = {
'method': 'GET',
'uri': testHost + '/allweather'
};
// subsequesnt request to get updated weather data
requestWithSession(options, (err, res, body) => {
let found = false;
weatherData = JSON.parse(body);
// console.log('weatherData in test', weatherData);
weatherData.forEach(weather => {
if(weather.location && weather.location.name === 'New York') {
found = true;
}
});
expect(found).to.be.true;
done();
});
});
});
}); // Weather Data
Here is the terminal output:
Can anyone please tell me what am I doing wrong?
When you run your test is that the test suite make a request to your test server, and the code that handles the request in your test server makes another request to another host.
You do not get to see 'Weather data received body' because the request handled by your test server is not waiting for the request that the test server itself makes. addNewCityWeatherData has no callback and does not return a promise, so the code that calls it goes on its merry way without waiting for it to complete. You should modify it to allow for the calling code to wait for a result.
Also, I'm not seeing how the data from the request initiated by your test server is folded back into the request that comes from your test suite. You may have to add some code for that too, unless addWeatherDataToDB(city, data); is taking care of it automatically somehow.
I have this code, which is returns the following error on req.body.firstname.length inside router.use on the page load:
TypeError: Cannot read property 'length' of undefined
Because already checking the input forms value and the default value is undefined. I want to only check this code when it's getting req.xhr request from the AJAX form, not on the page load. Is there a way to achieve this?
router.use((req, res, next) => {
if(req.body.firstname.length === 0) {
var validateFirstname = false;
} else {
var validateFirstname = true;
};
if(validateFirstname === true && validateCaptcha === true) {
console.log('SUCCESS: Form validated!');
} else {
console.log('ERROR: Form not validated!');
};
next();
});
The reason why it's in a router.use, not in router.post, because I want to share this logic for router.get also, where my template rendering resides.
UPDATE:
Here is the relevant full code to get the idea what's going on:
/////////////////////////////////////////////////////////////
// Nodemailer MIDDLEWARE
/////////////////////////////////////////////////////////////
router.post('/', (req, res, next) => {
const firstname = req.body.firstname;
req.firstname = firstname;
if(req.firstname.length === 0) {
var validateFirstname = false;
} else {
var validateFirstname = true;
};
if(validateFirstname === true) {
console.log('SUCCESS: Form validated!');
// send mail with defined transport object
req.sending = true;
} else {
console.log('ERROR: Form not validated!');
req.sending = false;
};
next();
});
router.use((req, res, next) => {
// create reusable transporter object using the default SMTP transport
req.transporter = nodemailer.createTransport({
...
});
// setup e-mail data with unicode symbols
req.mailOptions = {
...
};
next();
});
/////////////////////////////////////////////////////////////
// Nodemailer
/////////////////////////////////////////////////////////////
router.post('/', (req, res, next) => {
if(!req.xhr && req.sending === true) {
// send mail with defined transport object
req.transporter.sendMail(req.mailOptions, (err, info) => {
if(err) {
console.log('Error occurred');
console.log(err.message);
}
console.log('Message sent successfully!');
console.log('Server responded with "%s"', info.response);
res.send('<div class="form-validation-success">SUCCESS: Message sent successfully!</div>');
});
} else {
res.send('<div class="form-validation-error">ERROR: Message cannot be sent!</div>');
};
/////////////////////////////////////////////////////////////
// INTERNAL API
// GET Contact page
/////////////////////////////////////////////////////////////
router.get('/', (req, res, next) => {
res.render('contact', {
title: 'Contact',
formValidationError: req.displayBlock
});
});
In my handebars template:
<div id="responseText"></div>
This code is now working but not updating the already rendered viewing template. I guess to only way to solve this to handle the server's response with AJAX, but how? This is the client-side AJAX code:
'use strict';
(function() {
document.getElementById('xhr').onclick = function() { makeRequest(); };
function makeRequest() {
var firstname = document.getElementById('firstname').value;
var data = { firstname: firstname };
// instance of a class that provides this functionality
var xhr = new XMLHttpRequest();
// decide what you want to do after you receive the server response to your request
xhr.onreadystatechange = function() {
try {
// process the server response
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
// everything is good, the response is received
//alert(xhr.responseText);
document.getElementById('responseText').innerHTML = xhr.responseText;
} else {
// still not ready
alert('There was a problem with the request.');
};
} catch(e) {
console.log('Caught Exception: ' + e.description);
};
};
// make the request
xhr.open('POST', '/en/contact', true);
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.send(JSON.stringify(data));
};
})();
It's always returning the There was a problem with the request error message, even when also returning xhr.responseText.
This router file resides at routes/contact.js and handling the requests at /:lang/contact. That's why you see '/' url in the file.
can you please add some more information on it....as it is very difficult to figure out the problem with this information.
you are trying a post on uri /en/contact
but receiving a request on /
For a personal project I'm trying to create a simple oEmbed system using Nodejs.
My route looks like this:
app.get('/oembed/:url', function (req, res) {
oembed.get(req.params.url, function (error, result) {
return res.json(200, {message: "OK"});
});
});
and oembed is exposed using var oembed = require('../oembed');
For the oembed.js itself I have:
var request = require('request');
exports.get = function(url, callback) {
//this bit will be developed to call different functions depending on the URL
html = vimeo(url)
};
function vimeo(url) {
var videoUrl = url;
var endpoint = 'http://www.vimeo.com/api/oembed.json';
var url = endpoint + '?url=' + encodeURIComponent(videoUrl) + '&width=640';
request(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
var video = JSON.parse(body);
return video.html
}
})
}
So far, the vimeo function returns the desired html to the function call but I'm a bit lost from here. I'm not sure how I return the html to the initial request (the oembed.get part) and utilise the callback's error and result parameters.
Any help (and advice) would be much appreciated.
It seems you just don't understand how callback functions work. You need something like this in your oembed.js file:
var request = require('request');
exports.get = function(url, callback) {
var endpoint = 'http://www.vimeo.com/api/oembed.json';
request(endpoint + '?url=' + encodeURIComponent(url) + '&width=640', function (error, response, body) {
if (!error && response.statusCode == 200) {
try {
callback(null, JSON.parse(body).html);
} catch (error) {
callback(error);
}
}
});
};
And your route should look like this:
app.get('/oembed/:url', function (req, res) {
oembed.get(req.params.url, function (error, result) {
if (error) {
res.json(500, {message: error});
} else {
res.json(200, {message: "OK"});
}
});
});