I have a form whose submit button calls
exports.postUpdateProfile = function(req, res, next) {
User.findById(req.user.id, function(err, user) {
if (err) return next(err);
user.email = req.body.email || '';
user.profile.name = req.body.name || '';
//check req.body.rescueTimeKey by making http request,
//and parsing response to ensure the key is good
user.profile.rescueTimeKey = req.body.rescueTimeKey;
user.save(function(err) {
if (err) return next(err);
req.flash('success', { msg: 'Profile information updated.' });
res.redirect('/account');
});
});
};
I want to make sure the req.body.rescueTimeKey is valid before saving the profile, I've tried to create a module to perform that check...
//rescue-time.js module
var request = require('request');
exports.validKey = function(key){
var options = {
url: 'https://www.rescuetime.com/anapi/data?key=' + key,
json: true
};
var cb = function callback(error, response, body){
if(error || body.error){
//Key is bad, flash message and don't allow save
}
//key is good, save profile
};
request(options, cb);
}
As you might imagine I am not fully grasping the node style of using callbacks when making async calls, any help to re-organize this code is greatly appreciated.
What you will want to do is add an extra argument to your validKey function to accept a callback which is what we will use after the request.
So your rescue-time.js will look something like this:
// rescue-time.js
var request = require('request');
exports.validKey = function(key, cb) {
var options = {
url: 'https://www.rescuetime.com/anapi/data?key=' + key,
json: true
};
request(options, function (error, response, body) {
if(error || body.error){
cb(false)
}
else {
cb(true);
}
});
};``
We're returning a boolean result of true or false if the key is valid.
Inside your controller you will want something like the following:
var rescueTime = require('./path/to/rescue-time.js');
exports.postUpdateProfile = function(req, res, next) {
User.findById(req.user.id, function(err, user) {
if (err) return next(err);
user.email = req.body.email || '';
user.profile.name = req.body.name || '';
//check req.body.rescueTimeKey by making http request,
//and parsing response to ensure the key is good
user.profile.rescueTimeKey = req.body.rescueTimeKey;
// We're sending in a callback function that will have a "valid" result as a second arg
rescueTime.validKey(user.profile.rescueTimeKey, function(valid) {
// check if valid returned true or false and act accordingly
if (!valid) {
req.flash('error', 'invalid rescueTime key');
res.redirect('/account');
}
else {
user.save(function(err) {
if (err) return next(err);
req.flash('success', { msg: 'Profile information updated.' });
res.redirect('/account');
});
}
});
});
};
Keep in mind this code wasn't tested at all, but more of an example on what you need to do to obtain your desired results.
Related
I have a fairly extensive Node.js back-end with Express and pg-promise. Most of it is working perfectly as I expected it to.
Currently I am trying to create a function that deletes from multiple tables if I pass in a value for it to do so. My problem is that when I try to use req.body.cont (cont is a variable to continue) I get undefine
I have tried changing the names of the variables, trying to mimic how I sent data to the server before.
function removeUser(req, res, next) {
console.log(req.body.cont);
if (req.body.cont) {
console.log('hello');
deleteLocCustId(req, res, next, req.params.id);
}
db.result('delete from customer where id = $1', req.params.id)
.then(function(result) {
if (!req.body.cont) {
res.status(200).json({
status: 'success',
message: `Removed ${result.rowCount} customer`
});
}
})
.catch(function(err) {
console.log(err);
return next(err);
});
}
When I use this same method to create a customer it works perfectly.
function createCustomer(req, res, next) {
const newCustomer = new PQ(
'insert into customer(customer_name, ID, parent, symID) values ($1, $2, $3, $4)'
);
newCustomer.values = [
req.body.customerName,
req.body.cId,
req.body.parentName,
req.body.symPID
];
db.none(newCustomer)
.then(() => {
if (req.body.locationObject) {
return createLocation(req, res, next);
}
return null;
})
.catch(function(err) {
return next(err);
});
}
When I try to console.log(req.body.cont); I get undefined from my server.
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?
I have connected my Database to auth0 and when I try the connection is returns 401 unauthorized access. I have allowed auth into my firewall and the password is correct. How come it is returning this error when searching for username and password?
More info
in my easy tables I made them authenticated access only, do I have to do something to get around this?
function login(email, password, callback) {
//this example uses the "tedious" library
//more info here: http://pekim.github.io/tedious/index.html
var Connection = require('tedious#1.11.0').Connection;
var Request = require('tedious#1.11.0').Request;
var TYPES = require('tedious#1.11.0').TYPES;
var connection = new Connection({
userName: 'username',
password: 'pass',
server: 'server',
options: {
database: 'db',
encrypt: true,
rowCollectionOnRequestCompletion: true
}
});
var query = "SELECT Id, Email, Password " +
"FROM user WHERE Email = #Email";
connection.on('debug', function (text) {
// Uncomment next line in order to enable debugging messages
// console.log(text);
}).on('errorMessage', function (text) {
console.log(JSON.stringify(text, null, 2));
return callback(text);
}).on('infoMessage', function (text) {
// Uncomment next line in order to enable information messages
// console.log(JSON.stringify(text, null, 2));
});
connection.on('connect', function (err) {
if (err) { return callback(err); }
var request = new Request(query, function (err, rowCount, rows) {
if (err) {
callback(new Error(err));
} else if (rowCount < 1) {
callback(new WrongUsernameOrPasswordError(email));
} else {
bcrypt.compare(password, rows[0][2].value, function (err, isValid) {
if (err) { callback(new Error(err)); }
else if (!isValid) { callback(new WrongUsernameOrPasswordError(email)); }
else {
callback(null, {
user_id: rows[0][0].value,
email: rows[0][1].value
});
}
});
}
});
request.addParameter('Email', TYPES.VarChar, email);
connection.execSql(request);
});
}
Since you are using Azure Mobile App, which included the Node.js server SDK for your app. Then you don't need to install tedious to work with Azure SQL database. The SDK has already wrapped up mssql to do this. So basically you can use this code sample to connect your database.
var api = {
// an example of executing a SQL statement directly
get: (request, response, next) => {
var query = {
sql: 'UPDATE TodoItem SET complete = #completed',
parameters: [
{ name: 'completed', value: request.query.completed }
]
};
request.azureMobile.data.execute(query)
.then(function (results) {
response.json(results);
});
},
// an example of executing a stored procedure
post: (request, response, next) => {
var query = {
sql: 'EXEC completeAllStoredProcedure #completed',
parameters: [
{ name: 'completed', value: request.query.completed }
]
};
request.azureMobile.data.execute(query)
.then(function (results) {
response.json(results);
});
}
};
module.exports = api;
For more information, please refer to this documentation.
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 /
I'm doing an HTTP post, which returns single 10-15 character string. I want to assign this value to a temporary variable and use it while building up another larger string which will contain said 10-15 digit character string.
I've gotten this to work by making the temp variable ("ticket") global, which I don't like:
var ticket; // Global. Lame.
...stuff happens
getTicket("someUser", function(err) {
if(err)
{console.log("problem");}
else
{console.log(ticket);}
});
...other stuff happens
// Helper functions down here....
var getTicket = function (userName, callback) {
var user = userName;
request.post(
{
url: 'http://somewhere',
form: { 'username': user }
},
function(err, response, body) {
if(err) {
callback(err);
return;
} else {
ticket = body;
}
callback(null);
});
}
Can someone point me to the proper pattern to return the variable ticket in the POST's callback (or the body variable, whatever) ala:
_ticket = getTicket("someuser")
Thanks much.
You'd pass ticket as a parameter into your callback:
getTicket("someUser", function(err, ticket) {
if(err)
{console.log("problem");}
else
{console.log(ticket);}
});
function getTicket (userName, callback) {
var user = userName;
request.post(
{
url: 'http://somewhere',
form: { 'username': user }
},
function(err, response, body) {
if(err) {
callback(err);
return;
}
callback(null, body); // body contains the ticket
});
}
request.post is async, so there isn't a pattern that will get you something like:
_ticket = getTicket("someuser")