This loop is supposed to be checking for opps in the DB that match opps being pulled from the salesforce api results and then either creating a new opp or finding the existing opp and pushing it to the array. It seems that the res.render is running before the opp is found. It creates a new opp but the array returns empty when the page renders.
Account.find({owner:req.user._id, prospect:'false'}).sort({ammount:'desc'}).populate({path: "notes", options:{ sort:{ 'date': -1 } } }).exec(function(err, allAccounts) {
let callGoal = req.user.callGoal;
if(err){
res.send(err);
}else{
// if auth has not been set, redirect to index
if (!req.session.accessToken || !req.session.instanceUrl) { res.redirect('/'); }
//SOQL query
let q = "SELECT Id,Amount,CloseDate,LastActivityDate,Name,StageName,account.Name FROM Opportunity WHERE CloseDate < 2018-10-01 AND OwnerId = '0050a00000J12PdAAJ' AND IsClosed = false AND StageName != 'Stage 6: Won'";
//instantiate connection
let conn = new jsforce.Connection({
oauth2 : {oauth2},
accessToken: req.session.accessToken,
instanceUrl: req.session.instanceUrl
});
//set records array
let softopps = [];
let sfOpps = [];
let query = conn.query(q)
.on("record", function(record) {
sfOpps.push(record);
})
.on("end", function() {
console.log("total in database : " + query.totalSize);
console.log("total fetched : " + query.totalFetched);
let user = req.user;
sfOpps.forEach(function(sfopp){
if(err){
res.send(err);
}else{
Opp.findOne({sfid:sfopp.Id}).exec(function(err, opp){
if(!opp.length){
Opp.create(req.body, function(err, newOpp) {
if(err){
res.send(err)
}else{
newOpp.sfid = sfopp.Id;
newOpp.name = sfopp.Name;
newOpp.owner = user.sfid;
newOpp.save();
return softopps.push(newOpp)
}
})
}else{
return softopps.push(opp);
}
})
}
})
res.render("myaccounts", {accounts:allAccounts, callGoal:callGoal, user:user, sfOpps:sfOpps, opps:softopps});
})
.on("error", function(err) {
console.error(err);
})
.run({ autoFetch : true, maxFetch : 4000 });
}
});
Your Opp.findOne() and Opp.create() calls are asynchronous, so they fire after the res.render().
On the side, you pretty much kill your mongodb with all that unnecessary requests.
Try this logic instead (starting at .forEach)
Find all Opps by ids of sfOpps
Figure out which Opps were not found and create them
Concat all the found and not found Opps
Then, and only then respond
I didn't test that code, but it can roughly give you an idea of what I mean
Opp.find({ sfid : { $in: sfOpps.map(opp => opp.id) } })
.then(found => {
const foundIds = found.map(opp => opp.sfid)
const notFound = sfOpps.filter(opp => !foundIds.includes(opp.sfid)).map(sfopp => {
return {
sfid: sfopp.sfid,
name: sfopp.name,
owner: user.sfid
}
})
Opp.insertMany(notFound)
.then((insertResult) => {
res.render("myaccounts", {
accounts: allAccounts,
callGoal: callGoal,
user: user,
sfOpps: found.concat(notFound),
opps: softopps
});
}).catch(handleError)
}).catch(handleError)
When the page rendered your still empty because the call of creation/find opps is an asynchronous but your page rendering is synchronous that's why your page rendered first before your calls will be complete.I think you should try to use async.eachOfSeries(coll, iteratee, callback) instead of sfOpps.forEach().
Account.find({owner:req.user._id, prospect:'false'}).sort({ammount:'desc'}).populate({path: "notes", options:{ sort:{ 'date': -1 } } }).exec(function(err, allAccounts) {
let callGoal = req.user.callGoal;
if(err){
res.send(err);
}else{
// if auth has not been set, redirect to index
if (!req.session.accessToken || !req.session.instanceUrl) { res.redirect('/'); }
//SOQL query
let q = "SELECT Id,Amount,CloseDate,LastActivityDate,Name,StageName,account.Name FROM Opportunity WHERE CloseDate < 2018-10-01 AND OwnerId = '0050a00000J12PdAAJ' AND IsClosed = false AND StageName != 'Stage 6: Won'";
//instantiate connection
let conn = new jsforce.Connection({
oauth2 : {oauth2},
accessToken: req.session.accessToken,
instanceUrl: req.session.instanceUrl
});
//set records array
let softopps = [];
let sfOpps = [];
let query = conn.query(q)
.on("record", function(record) {
sfOpps.push(record);
})
.on("end", function() {
console.log("total in database : " + query.totalSize);
console.log("total fetched : " + query.totalFetched);
let user = req.user;
async.eachOfSeries(sfOpps,function(sfopp,next){
if(err){
next(err);
}else{
Opp.findOne({sfid:sfopp.Id}).exec(function(err, opp){
if(!opp.length){
Opp.create(req.body, function(err, newOpp) {
if(err){
next(err);
}else{
newOpp.sfid = sfopp.Id;
newOpp.name = sfopp.Name;
newOpp.owner = user.sfid;
newOpp.save();
softopps.push(newOpp);
next();
}
})
}else{
softopps.push(opp);
next();
}
})
}
},function(err){
if (err) res.send(err);
else {
res.render("myaccounts", {accounts:allAccounts, callGoal:callGoal, user:user, sfOpps:sfOpps, opps:softopps});
}
});
})
.on("error", function(err) {
console.error(err);
})
.run({ autoFetch : true, maxFetch : 4000 });
}
});
I hope this will help you.
Related
I want to look in a collection for every "open" project, than find for every project the signd in users in a different collection. But after the first .finally() the variable ´code = users´ is empty instead to save the users from the .foreach.
var code = "";
var project_details = "";
var users = "";
var endcode= "";
MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true }, (err, client) => {
if (err) throw err;
const db = client.db(dbname);
//find projects which are open
let collection = db.collection('project');
collection.find({ status: "open" }).forEach(function (project) {
//find users that signd in for that project
let collection = db.collection('users');
collection.find({ project: project.id }).forEach(function (user) {
users = user.name + users;
})
.catch((err) => { console.log(err); })
.finally(() => {
code = users;
});
//console.log(code); is empty and this point
//foreach project
project_details = "Project:" + project.name + ", Users: " + code;
endcode = endcode + project_details;
})
.catch((err) => { console.log(err); })
.finally(() => {
res.render('projects', { code: endcode });
client.close();
});
//////
});
The expected result in endcode should be "Project: Projectname1, Users: User1,User2 Project: Projectname2, Users: User3,User4 ....
How can I prevent from losing the written variable code?
With an HTML5 game I am developing at the moment, I get the player's username and ID through a token sent in by the client which are all stored in a database. Everything was working perfectly up until I had to actually set the values for the player.
I've been searching all over the internet for similar problems that I am facing, and have come up with nothing. I am not sure if this is a variable scope problem or if I am taking the wrong approach to doing this.
Anyways, here's both the socket.player and getUserSession function
var newUserID;
var newUsersName;
socket.player = {
id: newUserID,
x: randomInt(100,400),
y: randomInt(100,400),
username: newUsersName
}
function getUserSession(sessionKey, callback){
var sql = "SELECT * FROM game_sessions WHERE unique_key=? AND still_active=?";
var stillActive = 1;
var returnData = [];
db.query(sql, [sessionKey, stillActive], function(err, result){
if (err){
throw err;
}
var numFound = result.length;
if (numFound == 1) {
//session was found
var userID = result[0].user_id;
returnData.push(result[0].user_id);
var sql2 = "SELECT * FROM users WHERE id=?";
db.query(sql2, [userID], function(err, result){
if (err){
throw err;
}
var uName = result[0].username;
returnData.push(result[0].username);
return callback(returnData);
});
}
});
}
And the callback function:
getUserSession(data.token, function(result){
newUserID = result[0];
newUsersName = result[1];
socket.broadcast.emit('newclient',socket.player);
});
I would do something like this:
function sqlQuery(sql, params) {
return new Promise((resolve, reject) => {
db.query(sql, params, function(err, result) {
if (err) {
return reject(err);
}
return resolve(result);
});
});
}
function getUserSession(sessionKey, callback){
const sql = "SELECT * FROM game_sessions WHERE unique_key=? AND still_active=?";
const stillActive = 1;
let returnData = [];
try {
return sqlQuery(sql, [sessionKey, stillActive])
.then(result => {
if (result.length === 1) {
//session was found
const userID = result[0].user_id;
returnData.push(result[0].user_id);
const sql2 = "SELECT * FROM users WHERE id=?";
return sqlQuery(sql2, [userID])
.then(result => {
const uName = result[0].username;
returnData.push(result[0].username);
return callback(returnData);
})
}
})
} catch (err) {
//handle err
}
}
Edit: if the callback function being passed in is an aync function, you'll probably need to modify the above snippet to await it.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I'm working in a simple API Key authentication, I just want to verify the given key against the user provied key.
I have a seperate file with the function querying the database, and returning true/false and the user object.
But in my route.js file, the return object is undefined even tough in my auth.js file it isn't.
I tried making the the function in router.get an async function using express-promise-router and making the function an await return var user = await auth.verify(req.params.uid, req.get("token")) but I don't realy know how async works.
router.js
[...]
router.get('/list/:uid', function(req, res) {
var user = auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user) // <-- Undefined
if (user.status) {
res.send("Success")
} else {
res.status(403)
res.json({status: 403, error: "Unkown User / Token"})
}
})
[...]
auth.js
var db = require('./db')
var ObjectId = require('mongodb').ObjectId;
module.exports = {
verify: (uid, key) => {
try {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {throw err}else{status=1}
if (user.length <= 0) {throw "NotExistingExc"; status = 0}else{
usr = user[0];
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
} /* --> Is {
status: 1,
user: {
_id: d47a2b30b3d2770606942bf0,
name: 'Sh4dow',
groups: [ 0 ],
api: 'YWFiMDI1MGE4NjAyZTg0MWE3N2U0M2I1NzEzZGE1YjE='
}
}
*/
return returnObj;
})
} catch (e) {
console.error(e)
return {
status: 0,
user: {},
error: e
}
}
}
}
db.js (Idk if needed)
var MongoClient = require('mongodb').MongoClient
var state = {
db: null,
}
exports.connect = function(url, done) {
if (state.db) return done()
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) return done(err)
state.db = db
done()
})
}
exports.get = function() {
return state.db.db("database")
}
exports.close = function(done) {
if (state.db) {
state.db.close(function(err, result) {
state.db = null
state.mode = null
done(err)
})
}
}
I want to have the returnObjin auth.js in the router.get of my route.js file.
Make auth.verify return a Promise which we can then await for it inside router, You can just make the callback async no need for express-promise-router
router.get('/list/:uid', async function(req, res) {
try {
var user = await auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user)
if (user.status) {
res.send("Success")
} else {
res.status(403).json({status: 403, error: "Unkown User / Token"})
}
} catch (e) {
console.error(e)
res.status(/* */).json(/* */)
}
})
auth
module.exports = {
verify: (uid, key) => new Promise((resolve, reject) => {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {
reject(err)
return
} else {
status = 1
}
if (user.length <= 0) {
reject(new Error("NotExistingExc"))
return
} else {
usr = user[0]
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
}
resolve(returnObj);
})
}
}
In short, the reason you get undefined is because the code in auth.js is asyncronous. But you're really close. The toArray method in MongoDB returns a promise, so you need to make sure you return that promise and then use it in the router correctly.
In auth.js, make sure verify returns a promise - just add return!
return collection.find(query).limit(1).toArray(...)
And then, change your usage of the verify to the async/await you originally tried:
router.get('/list/:uid', async function(req, res) {
var user = await auth.verify(req.params.uid, req.get("token"))
// More code here...
})
My question is similar to this.
However, mine runs in Node.js and it seems like a bit more complicated.
The server side wasn't built by me, but someone else that I can't contact. And he wrote code very differently.
And I have db.js and it looks like this:
And routes/email.js it uses db.js like this:
And when I click a button. I get this error:
db.emailRequest is not a function
in db.js, at the end of the file. It originally had this:
module.exports = new dbHelper;
And my style to use db.js in routers.
db.get().query(sql, input, function(err,res){
//TODO:
});
But it didn't work. So, I changed the end of db.js like this:
exports.get = function(){
console.log("exports.get");
return pool;
}
And also added some code in app.js like this:
db.connect(function(err){
if(err){
console.log('Unable to connect to MariaDB');
process.exit(1);
}
});
What should I do?
The full code of db.js is here:
const mariadb = require('mariadb');
var pool;
exports.connect = function(done){
console.log("Trying to connect DB...");
pool = mariadb.createPool({
host: 'localhost',
user: 'root',
password: 'xxxxxxx',
database:"XXXXX",
connectionLimit: 5 // Why 5 ???
});
pool.getConnection()
.then(conn => {
console.log("DB connected. id: " + conn.threadId);
conn.end(); //release to pool
}).catch(err => {
console.log("DB failed connection: " + err);
});
}
function makeToken(){
console.log("makeToken()");
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
for(var i=0;i<32;i++){
text+=possible.charAt(Math.floor(Math.random()*possible.length));
}
return text;
}
function dbHelper() {
console.log("dbHelper()");
this.emailRequest = function(email,num){
console.log("emailRequest");
pool.getConnection().then(conn => {
conn.query("INSERT INTO email_verification(email, code) VALUES(?,?)",[email,num]);
conn.end(); //release to pool
})
.catch(err => {
console.log("not connected due to error: " + err);
});
}
// wait and process until getting return value because rv is needed.
this.verify = async function(email,num){
console.log("verify");
let conn;
var result = false;
try {
conn = await pool.getConnection();
// within 3minutes
const rows = await conn.query("SELECT count(*) FROM email_verificaiton WHERE email=? AND code=? AND req_time >= NOW() - INTERVAL 3 MINUTE",[email,num]);
if(rows[0]["count(*)"]>0){
result = true;
}
} catch (err) {
throw err;
} finally {
if (conn) conn.end();
}
return result;
}
this.verifyUpdate = function(email,num){
console.log("verifyUpdate");
pool.getConnection()
.then(conn => {
conn.query("UPDATE email_verification SET status = 1 WHERE email=? AND code=?",[email,num]);
conn.end(); //release to pool
})
.catch(err => {
console.log("not connected due to error: " + err);
});
}
// wait and process until getting return value because rv is needed.
this.emailRegister = async function(email,pass,nick,devid){
console.log("emailRegister");
let conn;
var result;
try {
conn = await pool.getConnection();
var rows = await conn.query("SELECT count(*) FROM email_verification WHERE email=? AND status = 1",[email]);
if(rows[0]["count(*)"]>0){
rows = await conn.query("SELECT count(*) FROM member WHERE email=?",[email]);
if(rows[0]["count(*)"]==0){
var token = makeToken();
rows = await conn.query("INSERT INTO member (email,password,username,device_id,login_method,token) VALUES(?,?,?,?,0,?)",[email,pass,nick,devid,token]);
if(rows["affectedRows"]>0){
result = {result:true, code:200, message: "success",data:[{email:email,token:token}]};
} else{
result = {result:false,code:401, message:"db error"};
}
}else {
result = {result:false,code:402, message:"already registered id"};
}
} else {
result = {result:false,code:403, meesage:"email not verified"};
}
} catch (err) {
throw err;
} finally {
if (conn) conn.end();
}
return result;
}
// wait and process until getting return value because rv is needed.
this.emailLogin = async function(email,pass,devid){
console.log("emailLogin");
let conn;
var result;
try {
conn = await pool.getConnection();
rows = await conn.query("SELECT * FROM member WHERE email=?",[email]);
if(rows.length==1){
if(rows[0]["password"]==pass){
var token = makeToken();
rows = await conn.query("UPDATE member SET device_id = ?, token = ? WHERE email=?",[devid,token,email]);
console.log(rows)
if(rows["affectedRows"]>0){
result = {result:true,message:"Sign up Success.", code:200, data:[{email:email,token:token}]};
} else{
result = {result:false,message:"db error",code:401};
}
} else {
result = {result:false,message:"wrong password",code:402};
}
}else {
result = {result:false,message:"not registered id",code:403};
}
} catch (err) {
throw err;
} finally {
if (conn) conn.end();
}
return result;
}
}
//module.exports = new dbHelper;
exports.get = function(){
console.log("exports.get");
return pool;
}
I need to do this to log into SalesForce Databases and pass a query. Now I will be passing a lot of queries on many routers of express.js and its a real pain to login in every router. Please let me know if you know how I can avoid this.
var conn = new jsforce.Connection({
oauth2 : salesforce_credential.oauth2
});
var username = salesforce_credential.username;
var password = salesforce_credential.password;
// I want to avoid this login on every router
conn.login(username, password, function(err, userInfo) {
if (err) {
return console.error(err);
}
conn.query("SELECT id FROM Sourcing__c WHERE id = 'req.session.ref'",function(err, result) {
if (err) {
return console.error(err);
}
if(result.records.length === 0){
req.session.ref = "";
}
var body = {
"Auth__c": req.user.id,
"Stus__c": "Pending - New Hire",
"Record": "012lvIAC",
"Sourcing__c": req.session.ref
};
conn.sobject("SFDC_Employee__c").create(body, function(err, ret) {
if (err || !ret.success) {
return console.error(err, ret);
}
console.log("Created record id : " + ret.id);
// ...
});
});
});
You may save the login status and check it every time when do a query,
here I add a property on conn (the instance of 'jsforce.Connection'), I'm not sure but I think there may be a method or property that show the login status of the 'conn', you may dig into its documents.
var conn = new jsforce.Connection({
oauth2 : salesforce_credential.oauth2
});
var username = salesforce_credential.username;
var password = salesforce_credential.password;
conn._isLogin = false;
// here in your route handler
if (!conn._isLogin) {
conn.login(username, password, function(err, userInfo) {
if (err) {
return console.error(err);
}
conn._isLogin = true;
doQuery(conn);
});
} else {
doQuery(conn);
}
function doQuery (conn) {
conn.query("SELECT id FROM Sourcing__c WHERE id = 'req.session.ref'",function(err, result) {
if (err) {
return console.error(err);
}
if(result.records.length === 0){
req.session.ref = "";
}
var body = {
"Auth__c": req.user.id,
"Stus__c": "Pending - New Hire",
"Record": "012lvIAC",
"Sourcing__c": req.session.ref
};
conn.sobject("SFDC_Employee__c").create(body, function(err, ret) {
if (err || !ret.success) {
return console.error(err, ret);
}
console.log("Created record id : " + ret.id);
// ...
});
});
}