Context in a bound node-mysql callback - node.js

I am defining a user object. If the user is logged in they have an ID of more than zero. Then
the conditional calls a node-mysql query and the results are assigned to the object properties. I have binded (bound?) the context in the node-mysql query callback to the "this" of the constructor(I think). My problem is when the user is not logged in and the ID is zero. The context is different. I know this because I copied the property assignments from the "else" part of the conditional into the "if" part and it worked. But I don't want to leave that because it would always query.
var Q = require("q");
var mysql = require('mysql');
function User(id, type) {
var deferred = Q.defer();
// If the user is logged in
if (id > 0) {
connection.query(query, function(err, results) {
if(err) {
deferred.reject(new Error(err))
} else {
var result = results[0];
this.id = result.id;
this.type = result.type;
this.nickname = result.nickname;
deferred.resolve(this);
}
}.bind(this));
} else {
this.id = 0;
this.type = 1;
this.nickname = "guest";
deferred.resolve(this);
}
return deferred.promise;
}
The object is instantiated here ... for example. The problem is when a user that is
not logged in (and has an ID of zero) is instantiated the object is empty, but otherwise
contains the properties.
var renderIndex = function(req, res) {
var localId = req.session.passport.user ? req.session.passport.user : 0,
options = {};
var user = new User(localId, 1);
return Q.all([
findGames(),
user
])
.then (function(allArray) {
var gamesObj = allArray[0],
userObj = allArray[1];
options.title = 'Title';
options.games = gamesObj;
options.user = userObj;
// Render the index jade template
res.render('index', options);
})
.done();
};

Related

updating related models from parent model

I am trying to create a related model via the parent model using the uRL of the parent model
Parent : Applications
Related : Applicants
This is the below relationship
applications --> hasMany -- > applicants ( foreignkey : application
id )
applicants --> BelongsTo --> applications (foreignkey : applicationid )
I want to do an upsert on both the models togther using the url of the parent model
PATCH /api/applications
Below is the applications.js file
module.exports = function(Application)
{
Application.afterRemote('upsert', function(ctx, instance, next){
var response ={};
response.id = ctx.result.id;
var applicants = ctx.req.body.applicants || undefined;
Application.getApp(function (err, app) {
var resp = { "applicants" :[]};
if (applicants){
for (var i=0; i<applicants.length ; i++)
{
applicants[i].application_id = ctx.result.id;
app.models.Applicants.upsert(applicants[i], function (err, result)
{
if (err) throw err;
if (!result) {
var err = new Error("Insert into Applicant failed");
err.statusCode = 500;
next(err);
}
// This prints the result in the console.
console.log('***** In APP' + JSON.stringify(result));
resp.applicants.push(result);
});
console.log(JSON.stringify(applicants));
}
// This still results in a empty array
response.applicants = resp.applicants;
ctx.result = response;
}
});
next();
});
How can I fetch the result of the upsert to the applicants model and then send it back in the application response for the api.
Thanks
This is what I would do. There is certainly a better way thought but it might be a good start.
'use strict';
var app = require('../../server/server');
var models = app.models;
var Applicants;
app.on('started', function () {
Applicants = models.Applicants;
});
module.exports = function (Application)
{
Application.afterRemote('upsert', function (ctx, instance, next) {
var response = {};
response.id = ctx.result.id;
var applicants = ctx.req.body.applicants || undefined;
if (applicants) {
var resp = {"applicants": []};
var count = 0;
for (var i = 0, applicantsLength = applicants.length; i < applicantsLength; i++) {
applicants[i].application_id = ctx.result.id;
Applicants.upsert(applicants[i], function (err, result) {
if (err)
return next(err);
if (!result) {
var err = new Error("Insert into Applicant failed");
err.statusCode = 500;
return next(err);
}
resp.applicants.push(result);
count++;
if (count === applicantsLength) {
response.applicants = resp.applicants;
ctx.result = response;
next();
}
});
}
} else {
next();
}
});
};
If this is not working, you should look for the 'before save' hook.

Callback function with Nodejs + Mongoose, How to return only the search value?

I already researched how to do this, but I still can not understand, where am I going wrong ?
I researched how to do this type of function but I could not understand how to get an answer in my callback, always end up having 2 functions within the other.
Controller UserCtrl
// Models
var User = require('../models/user');
var isUserSearch = function(email,callback){
User.find({email:email},function(err,data){
if(err) throw err;
return callback(data);
});
}
var isUser = function(email){
var resp = isUserSearch(email,function(data){
return data;
console.log(data); // I get my data
});
console.log("Response : " + resp); // undefined
return resp;
}
var result = {
gerarToken : gerarToken,
isUser : isUser,
}
module.exports = result;
Model
// Model
var mongoose = require('mongoose');
// Schema
var Schema = mongoose.Schema({
name : {
type : String,
require : true
},
email : {
type : String,
require : true,
unique : true
},
password : {
type : String,
required : true
},
type : {
type : Number,
required : true,
default : 1
},
created : {
type : Date,
default : Date.now
}
});
var Data = mongoose.model('User', Schema);
module.exports = Data;
Context AuthCtrl
// Controllers
var Crypto = require('./cryptoCtrl');
var User = require('./UserCtrl');
// ----------- Login
var login = function(req,res){
var data = req.body;
var email = data.email;
var password = Crypto.cryptoString(data.password);
var existUser = User.isUser(email);
if(existUser){
// IsUser is a function to return the user array
// if it exists, otherwise it returns only a false
// boolean value. In the example I'm going to use this function
}
}
var resp = isUserSearch(email,function(data){
return data;
console.log(data); // I get my data
});
console.log("Response : " + resp); // undefined
resp is undefined due to non-blocking asynchronous architecture node.js provide.
The line which you are trying to logging the value is executed when the function that return the data isUserSearch has not already finished.
Did you already try to call isUserSearch rather than isUser in you controller?
var login = function(req,res){
var data = req.body;
var email = data.email;
var password = Crypto.cryptoString(data.password);
User.isUser(email, function(existUser) {
if(existUser){
console.log('User exist', existUser);
// IsUser is a function to return the user array
// if it exists, otherwise it returns only a false
// boolean value. In the example I'm going to use this function
} else {
console.log('User does not exist');
}
});
}
Then you can remove isUser and change:
var result = {
gerarToken : gerarToken,
isUser: isUserSearch,
}
You only need the isUserSearch function with the callback. Use the data returned in a callback in the AuthCtrl and you call this as:
Controller UserCtrl:
// Models
var User = require('../models/user');
var isUserSearch = function(email, callback){
/* use findOne() which returns a single document that you can check if null */
User.findOne({email: email}, function(err, user){
if (err) throw err;
return callback(!!user); // return a callback with a Boolean value as argument
});
}
var result = {
gerarToken : gerarToken,
isUser: isUserSearch,
}
module.exports = result;
Context AuthCtrl:
// Controllers
var Crypto = require('./cryptoCtrl');
var User = require('./UserCtrl');
// ----------- Login
var login = function(req, res){
var data = req.body;
var email = data.email;
var password = Crypto.cryptoString(data.password);
/* Call the isUser function with a callback */
User.isUser(email, function(userExists){
if (userExists) {
// userExists is a boolean value
}
});
}

How to Iterate over array using async and foreach

I am trying to create an api that will create a group with members in expressjs. This is how it works till now:
Make a post request with a JSON object in req.body, using which I will create a new group. If the members array of the req.body object contains member id, add it to the group members array, else create a new user and then add its id to the array.
Basically an existing user just gets added, new user will be created and then added. For this I need to loop through the members array in the req.body and check for an object.
But the code below doesn't seem to work properly. I am getting strange results for console.log(group_info.members);. I am expecting this to contain objects with id in an array, but getting random results. Something seems to be wrong in the foreach loop. Please help me figure it out.
var express = require('express');
var router = express.Router();
var Group = require('../models/GroupModel');
var User = require('../models/UserModel');
var async = require("async");
router.post('/', function (req, res, next) {
var group_members = [];
var group_info = req.body;
//see if a member object is sent, create user for that else just add the user id to group_members array
async.forEach(group_info.members, function (member, callback) {
if (typeof member == "object") {
//create new user and add the _id to members array
var user = new User(member);
user.save(function (err) {
if (err) return res.status(500).send(err);
var member_object = {id: user._id};
group_members.push(member_object);
}).then(callback);
} else {
var member_object = {id: member };
group_members.push(member_object);
callback();
}
}, function (err) {
//final call back
group_info.members = group_members; //replace the original array in request body with the new array of users
console.log(group_info.members);
var group = new Group(group_info);
group.save(function (err) {
if (err) return res.status(500).send(err);
res.json(group);
});
});
});
Looks like you made a mistake its eachSeries not forEach, so just replace :
async.forEach(group_info.members, function (member, callback)
with:
async.eachSeries(group_info.members, function (member, callback)
Update
As pointed out in the comments forEach is an alias for async each API, You can read the docs here,Thank You #megawac for pointing this out.
var group_members = [];
var group_info = req.body;
var todo = group_info.members.length;
var done = 0;
if(todo == 0)
{
return saveGroup(res, group_members);
}
else
{
group_info.members.forEach(function (member, index, array) {
if (typeof member == "object") {
//create new user and add the _id to members array
var user = new User(member);
user.save(function (err, savedObject) {
if (err || !savedObject)
{
return res.status(500).send(err);
}
else
{
var member_object = {id: savedObject._id};
group_members.push(member_object);
if(++done >= todo)
{
return saveGroup(res, group_info, group_members);
}
}
});
} else {
var member_object = {id: member };
group_members.push(member_object);
if(++done >= todo)
{
return saveGroup(res, group_info, group_members);
}
}
});
}
function saveGroup(res, group_info, group_members)
{
group_info.members = group_members; //replace the original array in request body with the new array of users
console.log(group_info.members);
var group = new Group(group_info);
group.save(function (err) {
if (err) return res.status(500).send(err);
res.json(group);
});
}

unable to return in node.js from a function

I have three blocks of code where block one executes first and the result of first block is passed to bloack 2 and then the final result is then passed to the third block which has to send data to the route.
But at the end the return is undefined.
function getUserKey(userRole, callback) {
//keys value is stored and returned
var keys = base.menuModel.find({ 'name' : userRole }, function (err, result) {
if (!err) {
var menu = JSON.stringify(result);
menu = JSON.parse(menu);
var menuKeys = [];
for(i = 0;i < Object.keys(menu[0].permissions[0]).length;i++) {
menuKeys.push((Object.keys(menu[0].permissions[0])[i]));
}
callback(null,menuKeys);
//returns menukeys to be shown
}
else {
return err;
}
});
}
n is holding the menu keys
function userMenuData(n, callback) {
var filterResult = base.globalMenuModel.find({"title" : { $in : n}},function (err, result) {
if (!err) {
callback(null,result);
}
else {
return err;
}
});
}
var userMenu = function(userRole,callback) {
var userMenuTemp = async.compose(userMenuData, getUserKey);
var sendData = userRole is passed and the result is obtained
userMenuTemp(userRole,function(err,result) {
return result; // data success
});
console.log(sendData); //undefined
return sendData;
}
here i want to pass sendData to route in node.js
but at the console i am getting undefined.
Thanks for any help
It's the async nature of node that's getting you. The console.log is happening before any of those functions are returned. You want to check out a library that does promises like Q http://documentup.com/kriskowal/q/

Promise to retrieve Timeline

Using Parse.com for few months now, I started to create some Cloud Function To let me create and retrieve some element in a TimeLine
I need to get my information from timeline's class and once I retrieve elements, I have to call getStat to add in the same object Widgets both information from my query and from the results from getStat.
I have tried to use Promise to achieve this, but not sure it was the best way to deal with it. Any suggestions / help ?
Parse.Cloud.define("getTimeline", function(request, response) {
var user = request.params.user;
var limit = request.params.limit;
var page = request.params.page;
if (limit === undefined) limit = 10;
if (page === undefined) page = 0;
var Timeline = Parse.Object.extend("timeline");
var query = new Parse.Query(Timeline);
query.equalTo("user", user);
query.descending('createdAt');
query.limit(limit);
query.skip(page * limit);
var Widgets = {};
query.find().then(function(results) {
var promise = Parse.Promise.as();
_.each(results, function(result) {
var Widget = new Object();
Widget.data = result;
var timeline_id = result.get('timeline_id');
promise = promise.then(function() {
return Parse.Cloud.run('getStats', {
'timeline_id': timeline_id
}).then(function(stat) {
Widget.stat = stat;
Widgets[timeline_id] = Widget;
});
});
});
return promise;
}).then(function() {
response.success(Widgets);
},
function(error) {
response.error("Error: " + error.code + " " + error.message);
});
});

Resources