I have the following piece of code in my server/fixtures.js file:
var userId = Accounts.createUser({
username: "tester",
email: "a#b.com",
password: "foobar",
profile: { name: "Max" }
});
var user = Meteor.users.findOne({_id: userId});
console.log(user.profile.name);
Now when I run meteor it logs undefined. What am I doing wrong?
I'm pretty sure I've had an Accounts.onCreateUser callback defined somewhere that was responsible for this. My bad!
I think you need to use Meteors Publications & Subscriptions. Check this link
for more info.
Example:
if (Meteor.isServer){
Meteor.publish('userdata', function() {
if(!this.userId) return null;
return Meteor.users.find( this.userId );
});
}
if (Meteor.isClient){
Meteor.subscribe('userdata');
console.log(Meteor.user());
}
So, the problem is that console.log is not available on the server (where this file runs according to Meteor directory conventions.). If you would like to log to the server console, you can use Meteor._debug() which works the same as console.log. You may also want to consider wrapping your code in a Meteor.startup(function() {}); block so that it runs as soon as the server spins up.
Related
I want to have client-side access for a certain set of fields for ALL users while I would like to have access to even more fields for the current user only. How do I go about writing publish code to accomplish this?
Right from Meteor documentation:
Meteor.publish("userData", function () {
return Meteor.users.find({_id: this.userId},
{fields: {'other': 1, 'things': 1}});
});
And also:
Meteor.publish("allUserData", function () {
return Meteor.users.find({}, {fields: {'nested.things': 1}});
});
Hope this helps.
As mentioned above, the
Meteor.publish("userData", function () {
return Meteor.users.find({_id: this.userId},
{fields: {'other': 1, 'things': 1}});
});
and
Meteor.publish("allUserData", function () {
return Meteor.users.find({}, {fields: {'nested.things': 1}});
});
publish functions will push the data from the Users collection.
Subscribe with
Tracker.autorun(function () {
Meteor.subscribe("userData");
Meteor.subscribe("allUserData");
});
And the additional data will automatically go into the Users collection and be available in the Meteor.user() object.
My story with that:
I proceeded as documentation says, but encountered with weird behavior.
I had publish function, where I published whole profile and email object for current user (lets say userData) and just some subset for the other users (allUserData).
When I had -
Meteor.subscribe("allUserData");
Meteor.subscribe("userData");
On client side right after user logged in, I've received just allUserData data. Thats mean even for my logged in user (That user couldn't see his own email address). When I refresh browser, bug was fixed and I got properly allUserData for all users except one logged in, which has his proper userData (with mentioned email address).
What is interesting, if I changed the sequence of that subscriptions, bug was fixed.:
Meteor.subscribe("userData");
Meteor.subscribe("allUserData");
Putting into Meteor.autosubscribe(function () { }) doesn't changed anything.
Finally I tried put that subscription into Deps.autorun(function() { }) and explicitly add reactivity and the problem with sequence was resolved..:
Deps.autorun(function() {
Meteor.subscribe("allUserData", Meteor.userId());
Meteor.subscribe("userData", Meteor.userId());
// or
// Meteor.subscribe("userData", Meteor.userId());
// Meteor.subscribe("allUserData", Meteor.userId());
});
In publish function I just replace this.userId with userId from parameter.
With next bug which I encountered was, that I've got secret systemData object in profile user's object and that can see just admins, not regular logged in users. But although correct set publish function with 'profile.systemData': 0 that secret object could see all logged in users which looked into his profile object.
Probably it was because my publish function(s) somehow interfered with publish function in Meteor Account package:
// Publish the current user's record to the client.
Meteor.publish(null, function() {
if (this.userId) {
return Meteor.users.find(
{_id: this.userId},
{fields: {profile: 1, username: 1, emails: 1}});
} else {
return null;
}
}, /*suppress autopublish warning*/{is_auto: true});
Anyway I resolved it with help of method Account.onCreateUser() and adding systemData next to profile object, not into profile.
There starts my other problems :) see Meteor.loginWithPassword callback doesn't provide custom object in User accounts doc
PS: If I knew it at begin, I've put systemData object into special collection.
I'm quite new in Meteor and I got an issue: I added some information to users and I'd like to be able to see it in a view.
So I create my users, here is my event:
Accounts.createUser({
username: username,
email: email,
password: password,
firstname:firstname,
lastname:lastname,
chief:chief
},
function(error){
if(error){
alert(error.reason)
}else{
Router.go('home');
}
});
Accounts.onCreateUser(function(options,user){
user.firstname = options.firstname;
user.lastname = options.lastname;
user.chief = options.chief;
return user;
});
then I publish my collection to get access on my client side of the app:
Meteor.publish("personaldata", function () {
return Meteor.users.find({_id: this.userId});
});
And without doing anything else I got an issue on my app: there is no error message on my command prompt, but when I open my app I got an Ironn:Router message. Here is the message:
'organize your application'
Router.route('/', function () {
this.render('Home', {
data: function () {
return Items.findOne({_id: this.params._id});
}
});
});
Yes the whole part is the message. I tried to subscribe to the collection in 'home' to settle the issue but it doesn't work, the same message is displayed.Does someone know why this message is displayed ? How can I have access to those data? Isn't it the proper way?
Hope you guys can figure out what the problem is, thanks.
Found the problem...
As I'm stupid I put my publish in the lib directory, so this was on both the server and the client side... Meteor didn't know what to do with it on the client side.
Using velocity/jasmine, I'm a bit stuck on how I should test a server-side method requiring that there be a currently logged-in user. Is there a way to make Meteor think a user is logged in via stub/fake ?
myServerSideModel.doThisServerSideThing = function(){
var user = Meteor.user();
if(!user) throw new Meteor.Error('403', 'not-autorized');
}
Jasmine.onTest(function () {
describe("doThisServerSideThing", function(){
it('should only work if user is logged in', function(){
// this only works on the client :(
Meteor.loginWithPassword('user','pwd', function(err){
expect(err).toBeUndefined();
});
});
});
});
What you could do is add users just to your test suite. You could do this by populating these users in a the server-side test script:
Something like:
Jasmine.onTest(function () {
Meteor.startup(function() {
if (!Meteor.users.findOne({username:'test-user'})) {
Accounts.createUser
username: 'test-user'
... etc
Then, a good strategy could be to use the beforeAll in your test to login (this is client side):
Jasmine.onTest(function() {
beforeAll(function(done) {
Meteor.loginWithPassword('test-user','pwd', done);
}
}
This is assuming your test isn't logged in yet. You can make this more fancy by checking for Meteor.user() and properly logging out in an afterAll, etc. Note how you can handily pass the done callback to many of the Accounts functions.
Essentially, you don't have to mock a user. Just make sure you have the right users, with the correct roles, available in the Velocity/Jasmine DB.
Lets say you have a server side method like this:
Meteor.methods({
serverMethod: function(){
// check if user logged in
if(!this.userId) throw new Meteor.Error('not-authenticated', 'You must be logged in to do this!')
// more stuff if user is logged in...
// ....
return 'some result';
}
});
You do not need to make a Meteor.loginWithPassword before executing the method. All you got to do is stub the this.userId by changing the this context of the method function call.
All defined meteor methods are available on the Meteor.methodMap object. So just call the function with a different this context
describe('Method: serverMethod', function(){
it('should error if not authenticated', function(){
var thisContext = {userId: null};
expect(Meteor.methodMap.serverMethod.call(thisContext).toThrow();
});
it('should return a result if authenticated', function(){
var thisContext = {userId: 1};
var result = Meteor.methodMap.serverMethod.call(thisContext);
expect(result).toEqual('some result');
});
});
EDIT: This solution was only tested on Meteor <= 1.0.x
What are you testing and why does it require a user to be logged in? Most of the methods I have that need a user object I pass the user object into. This allows me to call from a test without actually being logged in. So in the actual running of the code I would pass...
var r = myMethod(Meteor.user());
but when running from the test I would call like...
it('should be truthy', function () {
var r = myMethod({_id: '1', username: 'testUser', ...});
expect(r).toBeTruthy();
});
I think that Meteor.server.method_handlers["nameOfMyMethod"] allows you to call/apply a Meteor method and supply this as the first parameter at least in the current version (1.3.3)
this.userId = userId;
Meteor.server.method_handlers["cart/addToCart"].apply(this, arguments);
i am trying to create an admin interface for my meteor project and for this i created a custom accounts register form which says
Accounts.createUser({
email: userEmail,
password: userPassword,
admin: true
})
and in my router.js code i have this
Router.route('/admin', {name: 'admin'})
var requireAdminLogin = function(){
if(!Meteor.user({admin: true})){
this.render('accessDenied')
}else{
this.next();
}
}
Router.onBeforeAction(requireAdminLogin, {only: 'admin'})
the problem is even when i change my register code to say that the new user signing up is not admin, i can still get to my admin page. Can anyone help? thank you
Meteor.user() doesn't take any arguments. You probably want:
if (Meteor.user() && Meteor.user().admin) {
// admin
} else {
// access denied
}
I also suspect that passing the admin option to Accounts.createUser doesn't do anything. On the server, you could do var userId = Accounts.createUser followed by Meteor.users.update(userId, {$set: {admin: true}});.
A package called houston:admin did exactly what I wanted to do.
In my Meteor app I use the default accounts package, which gives me the default login and registration functionality. Now I want to add an extra field to user, say nickname, and for the logged in user the possibility to edit this information.
For editing the profile I suppose I should be doing something like this:
Template.profileEdit.events({
'submit form': function(e) {
e.preventDefault();
if(!Meteor.user())
throw new Meteor.Error(401, "You need to login first");
var currentUserId = this._id;
var user = {
"profile.nickname": $(e.target).find('[name=nickname]').val()
};
Meteor.users.update(currentUserId, {
$set: user
}, function(error){
if(error){
alert(error.reason);
} else {
Router.go('myProfile', {_id: currentUserId});
}
});
}
});
But I doesn't store the info if I look in Mongo. Also when showing the profile, {{profile.nickname}} returns empty. What is wrong here?
Edit: added collections\users.js to show permissions:
Meteor.users.allow({
update: function (userId, doc) {
if (userId && doc._id === userId) {
return true;
}
}
});
Meteor.users.deny({
update: function(userId, user, fieldNames) {
return (_.without(fieldNames, 'profile.nickname').length > 0);
}
});
Yeah, I believe that should do the job, although I haven't actually run the code. The idea is certainly right.
The main things to be aware of are:
The necessity to allow the user doc to be edited from the client with an appropriate Meteor.users.allow() block on the server, assuming you're going to remove the "insecure" package (which you need to before doing anything in production).
The fact that "by default the server publishes username, emails, and profile", so you'll need to write a Meteor.publish function on the server and subscribe to it if you want to expose any other fields within the user document to the client once you've removed the "autopublish" package (which again, you really should).