createRecord not defined error (Firebase + Ember.js) - javascript

I was wondering if anyone could point me in the right direction and help me fix this error I'm getting when I attempt to add a user with my Ember.js model after created a user with Firebases createUser method.
To be more specific here is the error I'm getting: Uncaught TypeError: Cannot read property 'createRecord' of undefined
App.SignUpController = Ember.Controller.extend({
needs: ['sign-in'],
needs: ['application'],
userSignedIn: false,
actions: {
signMeUp: function() {
var state = false;
var controllerContext = this;
// Create firebase user
ref.createUser({
email : this.get('email'),
password : this.get('password'),
}, function(error, user) {
if (error === null) {
console.log('User created with id', user.uid);
state = true;
controllerContext.set('userSignedIn', state);
console.log("State from sign-up page: "+ state);
console.log("Testing user.uid inside: "+user.uid);
var fbid = user.id;
controllerContext.set('user id', user.uid);
var newUser = this.store.createRecord('user', {
id: fbid,
email: this.get('email'),
password: this.get('password'),
});
newUser.save();
} else {
console.log("Error creating account:", error);
}
}); // End createUser
this.transitionToRoute('letters');
}
}
});
UPDATE: Here is a (very hacky) solution I came up with after a day of JS plumbing.
App.SignUpController = Ember.Controller.extend({
needs: ['sign-in'],
needs: ['application'],
userSignedIn: false,
thisUserID: '',
actions: {
signMeUp: function() {
var state = false;
var controllerContext = this;
// Create firebase user
function authWithPassCallback(userObj, user){
console.log("authWithPassCallback user.uid is: "+user.uid);
return user.uid
}
function createUserAndLogin(userObj, callback) {
ref.createUser(userObj, function(error, user) {
if (error === null) {
console.log("User created successfully");
controllerContext.set('thisUserID', user.uid);
return callback(userObj, user);
} else {
console.log("Error creating user:", error);
}
});
}
var userAndPass = {
email: this.get('email'),
password: this.get('password')}
var fbPayload = createUserAndLogin(userAndPass, authWithPassCallback);
setTimeout(function () {
console.log("FB load: "+ controllerContext.get('thisUserID'));
var newUser = controllerContext.store.createRecord('user', {
id: controllerContext.get('thisUserID'),
email: controllerContext.get("email"),
password: controllerContext.get("password"),
});
newUser.save();
controllerContext.transitionToRoute('letters');
}, 1000);
console.log(controllerContext.get('thisUserID'));
}
}
});

I'm assuming the error is occurring at newUser = this.store.createRecord - at this point in your code this is no longer referring to the controller. You will need to use controllerContext.store.createRecord.

you probably just lost the context here. this doesn't refer to the controller, you're in the error function.
There are two ways of fixing that. First is to bind the function to the controller's this:
ref.createUser({
// ...
}, function(error, user) {
var newUser = this.store.createRecord('user', {/*...*/});
// ...
}.bind(this));
or to reuse the controllerContext variable:
ref.createUser({
// ...
}, function(error, user) {
// ...
var newUser = controllerContext.store.createRecord('user', {/*...*/});
});

Related

meteor server insert data without login

I want to create API that allow other app to insert(create) new data. But so far I tried, this not work with error message "User id is required", I know that happen because no login user found when insert new data. Is it possible to insert new data without login or any possible way to login from server side if using accounts-password's package or any possible way to make this work?
code on server
Picker.route('/createFood/:title', function(params, req, res, next) {
console.log('-> params : ',params);
let username = (new Date()).getTime().toString();
function createFood() {
Fiber(function() {
console.log("-> username : ",username);
let acc = Accounts.createUser({
username: username,
email: username +'#foodie.com',
password: username
});
if (acc) {
console.log("-> acc : ",acc);
// Meteor.call("setUserId", acc);
Menus.insert({
title: params.title,
}, function(err, foodId) {
console.log("-> abs : ",Meteor.absoluteUrl());
console.log("-> err : ",err.message);
console.log("-> foodId : ",foodId);
let code, result;
if (err) {
code = 500;
result = {
error: err.message
}
} else {
code = 200;
result = {
foodId: foodId,
}
}
res.setHeader( 'Content-Type', 'application/json' );
res.statusCode = code;
res.end( JSON.stringify( result ) );
})
}
}).run();
}
if (params.title)
createFood();
});
code food model, there is userId owner here
if (Meteor.isServer) {
Menus.allow({
insert: function() {
return true;
},
update: function() {
return true;
},
remove: function() {
return true;
},
fetch: ['foodId'],
});
Menus.after.insert((userId, doc) => {
....
})
}
There is no reason why you can't insert to the database without logging in. You don't even have to include the accounts package if you don't want to .
Your current code doesn't insert unless a user is present, you can simplify it to this...
function createFood() {
Menus.insert({
title: params.title,

Getting promise error in node js

Server js:
var user_controller = require('./controllers/user_controller');
passport.use(new passportLocal(function(username, password, done) {
user_controller.login(username, password).then(function(value) {
if (value) {
console.log(value + "true")
done(null, {
id: username,
name: username
});
} else {
console.log(value + "false");
done(null, null);
}
})
}));
USer contoller:
module.exports.login = function(username, password) {
var status;
var userid = username;
User.findOne({
'username': [userid],
'password': [password]
}, function(err, user) {
if (!user) {
console.log("logged err");
status = false;
} else {
console.log("login in");
status = true;
}
console.log(status);
return status;
});
};
I am doing an async task (db call) in my user_controller.login and it returns a boolean "value"
But when I execute this I am getting error:Cannot read property 'then' of undefined
I saw previous questions on this error but couldnt understand it
I think you can try with callback function. The login function takes one callback param and execute that function at end.
module.exports.login = function(username, password, callback) {
var status;
var userid = username;
User.findOne({
'username': [userid],
'password': [password]
}, function(err, user) {
if (!user) {
console.log("logged err");
status = false;
} else {
console.log("login in");
status = true;
}
console.log(status);
callback(status);
});
};
While calling the function login pass the third param as a callback function.
var user_controller = require('./controllers/user_controller');
passport.use(new passportLocal(function(username, password, done) {
user_controller.login(username, password, function(value) {
if (value) {
console.log(value + "true")
done(null, {
id: username,
name: username
});
} else {
console.log(value + "false");
done(null, null);
}
})
}));
Note: I am not tested the above code.
You can create promise based functions in Nodejs using Q library.
npm install q
Below code can help you.
module.exports.login=function(username,password){
var status;
var userid=username;
User.findOne({'username':[userid], 'password':[password]},function(err,user){
var deferred = Q.defer();
if(err){
deferred.reject(err);
} else{
deferred.resolve(data);
}
return deferred.promise;
});
and use it in Server.js
var user_controller = require('./controllers/user_controller');
passport.use(new passportLocal(function(username, password, done) {
user_controller.login(username, password).then(function(user) {
done(null, {
id: username,
name: username
});
});

How to spy on a class method in node-jasmine?

I have a module User - like this:
module.exports = User = (function() {
function User(params) {
this.id = params.id;
this.first_name = params.first_name || '';
this.last_name = params.last_name || '';
this.username = params.username;
this.email = params.email;
this.password = params.password;
};
User.findByUsername = function(username, callback) {
if (!_.isEmpty(username)) {
var opts = {table: TABLE, where: {sql: "username='"+username+"'"}};
QueryDispatcher.findWhere(opts, function(err, result) {
if(!_.isEmpty(err)) { return callback(err, null)}
callback(null, result.rows[0]);
});
};
};
return User;
};
The function that uses the class method:
module.exports = AuthStrategies = (function() {
AuthStrategies.localStrategy = function(username, password, done) {
async.waterfall([
function(callback) {
User.findByUsername(username, function(err, user){
if (err) { callback(err) };
if (_.isEmpty(user)) {
callback(null, false, { message: 'Incorrect username.' });
};
callback(null, user, null)
});
},
function(user, opts, callback) {
"do something here and call the next callback"
}]
, function(err, user, opts) {
if(err) { return done(err)}
if(!user) { return done(null, false, opts.message)}
done(null, user)
});
};
return AuthStrategies;
})();
I have my jasmine test -
var Auth = require('path to AuthStrategies module')
describe('Auth', function() {
describe('#AuthStrategies.localStrategy', function() {
describe('when user creds are valid', function() {
var test_user;
beforeEach(function(){
test_user = new User({
username: 'test996'
, password: 'password123'
, email: 'testemamil#email.com'
, first_name: ''
, last_name: ''
});
spyOn(User, "findByUsername").and.callFake(function(usrename, cb) {
cb(null, test_user);
});
});
it('returns user object', function(done) {
Auth.localStrategy('test996', 'password123', function(err, user, opts) {
expect(err).toEqual(null);
expect(user).toEqual(test_user);
done()
})
});
});
});
});
Essentially I want to stub out the User Class method findByUsername and fake the callback with my own results i.e nul error and a user(as if the find was successfully).
I have Spy on many "class" methods in my app and don't have this problem. This is baffling me a bit. The error only shows when I add .and.callThrough or .and.callFake to the spy.. the moment I remove this the test just times out ...which makes sense as the spy works and doesn't call the callback needed for the async waterfall to continue.
The error I am getting is -
So I figured it out -
The error you see above happens anyway. The reason I was getting the above "extra info" which was throwing me off btw - Was because I was running the test separately.
./node_modules/.bin/jasmine ./tests_to_run_spec.js
What would normal happen - is I would get a timeout failure due to a missing callback. as in my case above I wasn't calling the callback in the faked function I sup[plied properly.
actually even more interestingly - running jasmine-node from my PATH doesn't like the .and.... being called on this particular spy. Really have no idea. but that how I got the spyOn(User, 'findByUsername').and.callFake ... to work

Only getting partial user publication in Meteor Jasmine test

I have a client integration test to ensure my admin user can change user roles via the user management interface in my app. However, when I query for the user I want to change, the query comes back empty even though it has been created in the fixture.
describe('Admin users', function() {
beforeEach(function(done) {
Meteor.loginWithPassword('admin#gmail.com', '12345678', function(error) {
Router.go('/users');
Tracker.afterFlush(done);
});
});
beforeEach(waitForRouter);
afterEach(function(done) {
Meteor.logout(function() {
done();
});
});
it('should be able to change user roles', function(done) {
var changeUser = Meteor.users.findOne({ emails: { $elemMatch: { address: 'user#gmail.com' } } });
console.log('changeUser: ', changeUser);
console.log('Users: ', Meteor.users.find().fetch());
$('#user-' + changeUser._id + '-roles').val('manage-users').change();
expect(Roles.userIsInRole(changeUser, 'manage-users')).toBe(true);
expect(Roles.userIsInRole(changeUser, 'edit-any')).toBe(false);
done();
});
});
This test fails with the following error:
TypeError: Cannot read property '_id' of undefined
Here's the fixture file that creates the two users:
/* globals
resetDatabase: true,
loadDefaultFixtures: true,
*/
var Future = Npm.require('fibers/future');
resetDatabase = function () {
console.log('Resetting database');
// safety check
if (!process.env.IS_MIRROR) {
console.error('velocityReset is not allowed outside of a mirror. Something has gone wrong.');
return false;
}
var fut = new Future();
var collectionsRemoved = 0;
var db = Meteor.users.find()._mongo.db;
db.collections(function (err, collections) {
var appCollections = _.reject(collections, function (col) {
return col.collectionName.indexOf('velocity') === 0 ||
col.collectionName === 'system.indexes';
});
_.each(appCollections, function (appCollection) {
appCollection.remove(function (e) {
if (e) {
console.error('Failed removing collection', e);
fut.return('fail: ' + e);
}
collectionsRemoved++;
console.log('Removed collection');
if (appCollections.length === collectionsRemoved) {
console.log('Finished resetting database');
fut['return']('success');
}
});
});
});
return fut.wait();
};
loadDefaultFixtures = function () {
console.log('Loading default fixtures');
var adminId = Accounts.createUser({email: 'admin#gmail.com', password: '12345678'});
var standardUserId = Accounts.createUser({email: 'user#gmail.com', password: '12345678'});
console.log('Users: ', Meteor.users.find().fetch());
console.log('Finished loading default fixtures');
};
if (process.env.IS_MIRROR) {
resetDatabase();
loadDefaultFixtures();
}
I can see the output of the fixture console.log in the Jasmine logs, and it shows both users. The log from the test logs undefined for the changeUser and an array of only the current user for the full collection fetch.
The only other problems I can imagine are the publication and the subscription. I can't see anything wrong with them, but I could be missing it. Here's the publication:
Meteor.publish('allUsers', function () {
if (Roles.userIsInRole(this.userId, ['manage-users'])) {
return Meteor.users.find({}, { fields: { emails: true, roles: true, id: true}});
} else {
return this.ready();
}
});
and the subscription:
subscriptions: function() {
return [Meteor.subscribe('allUsers'), Meteor.subscribe('allRoles')];
},
It seems like the default Meteor users publication containing only the current user is being delivered for the test, but shouldn't waiting on the route and that route's user subscription mean that the entire user list is being published/subscribed?

Async waterfall equivalent with Q

I've got a single page which is an account settings page. In it, I allow my users to update their avatar (if they've attached an image), change their email (if it has been changed from the original), and change their name and password.
Right now, I'm using async's waterfall method, but am swapping out async for Q since I prefer the syntax (and api). I'm wondering if this is the way that I should be using Q in replacement of async's waterfall.
I'm doing something like this:
exports.settingsAccountPOST = function(req, res) {
var doesEmailExist = function() {
var deferred = Q.defer();
User.findByEmail({
email: req.body.email
}, function(err, user) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(user);
}
});
return deferred.promise;
};
var updateEmail = function(email) {
var deferred = Q.defer();
User.updateEmail({
userId : req.session.user.id,
email : req.body.email
}, function(err, updated) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(updated);
}
});
return deferred.promise;
};
var updateName = function() {
var deferred = Q.defer();
if (req.body.name) {
User.updateName({
userId: req.session.user.id,
name: req.body.name
}, function(err, updated) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(updated);
}
});
return deferred.promise;
}
};
doesEmailExist().then(function(email) {
if (!email) {
return(updateEmail(email));
}
}).then(function() {
return(updateName())
}).then(function() {
res.redirect('/account')
});
};
Say that there is an error with the email address being used. Is there a way to "pass" it to the final call? Use case: Updated password properly, but email update didn't work, so I want to show a session flash to the user telling them they updated their password properly, but there was an issue with updating their email.
I was looking in the docs and it seems I may need to use:
.fin(function () {
});
Is this correct? If so, what should I be passing into that? Just push to an object the error that occurred within the chain and then loop through all errors and display them to the user? Or just return immediately and display the error?
If you are using Q.defer you are generally doing something wrong.
var findByEmail = Q.nbind(User.findByEmail, User);
var updateEmail = Q.nbind(User.updateEmail, User);
var updateName = Q.nbind(User.updateName, User);
//later on...
exports.settingsAccountPOST = function (req, res) {
findByEmail({
email: req.body.email
})
.then(function (user) {
if (!user) {
return updateEmail({
userId: req.session.user.id,
email: req.body.email
});
}
})
.then(function () {
return updateName({
userId: req.session.user.id,
name: req.body.name
})
})
.then(function () {
res.redirect("/account");
})
.catch(function(e){
//Handle any error
});
};

Categories