How do I fetch user from pointer on Parse.com? - pointers

I need help using fetch() in CloudCode on Parse.com
RIght now everything I do throws an error that says " Cannot create a pointer to an unsaved ParseObject".
I am starting to think that CloudCode is broken because it doesn't make sense at all because I'm not creating anything new or creating pointers.
It's simple as
query.find().then(function(lists){
lists.forEach(elem, i){
elem.get('owner').fetch({
success: function(user){
console.log(user);
}
})
}
});
Where 'owner' is a pointer to a user object and the query is on a class that contains a Pointer (owner) and an Array of objects. Essentially I want to get the email of the owner to compare to the Array of objects that also contain emails.
Trying to use include with the query also doesn't work.

Related

Retrieve values from firebase database in conversation flow

I am trying to grab information from my firebase database after a particular intent is invoked in my conversation flow.
I am trying to make a function which takes a parameter of user ID, which will then get the highscore for that user, and then say that users highscore back to them.
app.intent('get-highscore', (conv) => {
var thisUsersHighestscore = fetchHighscoreByUserId(conv.user.id);
conv.ask('your highest score is ${thisUsersHighestScore}, say continue to keep playing.');
});
function fetchHighscoreByUserId(userId){
var highscoresRef = database.ref("highscores");
var thisUsersHighscore;
highscoresRef.on('value',function(snap){
var allHighscores= snap.val();
thisUsersHighscore = allHighscores.users.userId.highscore;
});
return thisUsersHighscore;
}
An example of the data in the database:
"highscores" : {
"users" : {
"1539261356999999924819020" : {
"highscore" : 2,
"nickname" : "default"
},
"15393362381293223232222738" : {
"highscore" : 78,
"nickname" : "quiz master"
},
"15393365724084067696560" : {
"highscore" : "32",
"nickname" : "cutie pie"
},
"45343453535534534353" : {
"highscore" : 1,
"nickname" : "friendly man"
}
}
}
It seems like it is never setting any value to thisUsersHighScore in my function.
You have a number of issues going on here - both with how you're using Firebase, how you're using Actions on Google, and how you're using Javascript. Some of these issues are just that you could be doing things better and more efficiently, while others are causing actual problems.
Accessing values in a structure in JavaScript
The first problem is that allHighscores.users.userId.highscore means "In an object named 'allHighscores', get the property named 'users', from the result of that, get the property named 'userId'". But there is no property named "userId" - there are just a bunch of properties named after a number.
You probably wanted something more like allHighscores.users[userId].highscore, which means "In an object named 'allHighscores', get the property named 'users', fromt he result of that, get the property named by the value of 'userId'".
But if this has thousands or hundreds of thousands of records, this will take up a lot of memory. And will take a lot of time to fetch from Firebase. Wouldn't it be better if you just fetched that one record directly from Firebase?
Two Firebase Issues
From above, you should probably just be fetching one record from Firebase, rather than the whole table and then searching for the one record you want. In firebase, this means you get a reference to the path of the data you want, and then request the value.
To specify the path you want, you might do something like
var userRef = database.ref("highscores/users").child(userId);
var userScoreRef = userRef.child( "highscore" );
(You can, of course, put these in one statement. I broke them up like this for clarity.)
Once you have the reference, however, you want to read the data that is at that reference. You have two issues here.
You're using the on() method, which fetches the value once, but then also sets up a callback to be called every time the score updates. You probably don't need the latter, so you can use the once() method to get the value once.
You have a callback function setup to get the value (which is good, since this is an async operation, and this is the traditional way to handle async operations in Javascript), but you're returning a value outside of that callback. So you're always returning an empty value.
These suggest that you need to make fetchHighScoreByUserId() an asynchronous function as well, and the way we have to do this now is to return a Promise. This Promise will then resolve to an actual value when the async function completes. Fortunately, the Firebase library can return a Promise, and we can get its value as part of the .then() clause in the response, so we can simplify things a lot. (I strongly suggest you read up on Promises in Javascript and how to use them.) It might look something like this:
return userScoreRef.once("value")
.then( function(scoreSnapshot){
var score = scoreSnapshot.val();
return score;
} );
Async functions and Actions on Google
In the Intent Handler, you have a similar problem as above. The call to fetchHighScoreByUserId() is async, so it doesn't finish running (or returning a value) by the time you call conv.ask() or return from the function. AoG needs to know to wait for an async call to finish. How can it do that? Promises again!
AoG Intent Handlers must return a Promise if there is an asyc call involved.
Since the modified fetchHighScoreByUserId() returns a Promise, we will leverage that. We'll also set our response in the .then() part of the Promise chain. It might look something like this:
app.intent('get-highscore', (conv) => {
return fetchHighscoreByUserId(conv.user.id)
.then( function(highScore){
conv.ask(`Your highest score is ${highScore}. Do you want to play again?`);
} );
});
Two asides here:
You need to use backticks "`" to define the string if you're trying to use ${highScore} like that.
The phrase "Say continue if you want to play again." is a very poor Voice User Interface. Better is directly asking if they want to play again.

Meteor: what is the right way to add custom settings object to users collection?

There are multiple examples on publish/subscribe but not clear on what is the best practice for storing custom data in the in-built "users" collection in Meteor (especially in the new possibility of template specific collections).
For example, I need to store user browse history - something that is accessible through Meteor.user().settings.history.lastvisited[]
The challenge is:
Is any special publish / subscribe required for the above? (the
reason being, I am assuming the users collection is already
published and available on client side - so do we need another?)
How to take care of edge cases where user is new and hence settings.history object may not be defined? Can we have a special publish that automatically takes care of creating an empty object if the settings is undefined? How to do it?
I did this :
// server side
Meteor.publish('userSettings', function (maxRows) {
if (this.userId) {
return Meteor.users.find({ _id: this.userId }, { fields: {'settings':1}});
}
this.ready();
});
//client side
Meteor.subscribe('userSettings');
But I do not see anyway how I can access the published "userSettings" object on the client side - what is missing ??
You can create a field and set it to false/'', on each user you create using the accountsOnCreateUser method.
Accounts.onCreateUser(function(options, user) {
//this function gets called each time a user has been created on the Meteor.user collection
if (options.profile)
user.settings = ''; //this is just and example.
return user;
})
Now the publish looks ok, but in order to get it work im always use a Tracker.autorun function.
Tracker.autorun(function(){
Meteor.subscribe('userSettings');
})
Why the autorun? well if you don't call the auto run here, the subscription get only called 1 time when the apps loads, and not when the user documents.
Take care of yours deny/allow permissions, check this meteor:common mistakes post on the Profile editing section
Also the subscribe function have a callback function. Meteor.subscribe(name, [arg1, arg2...], [callbacks]), so you can do something like this.
var myUserSubscription = Meteor.subscribe('userSettings',function(){
console.log("ok im here on the client side")
console.log("this user subscription is ready " + myUserSubscription.ready())
})
console.log("outside the subscription why not? " + myUserSubscription.ready();
About ready();
True if the server has marked the subscription as ready. A reactive
data source.

Meteor: Get count of collection by name. Accessing global scope on server

I'd like to create a method that returns the count of a generic collection.
Calling the method would look something like this:
Meteor.call('getCollectionCount', 'COLLECTION_NAME');
And the result would be the collection count.
The server method code would look something like this:
getCollectionCount: function (collectionName) {
return window[collectionName].find().count();
}
This won't work because window isn't defined on the server, but is something similar possible?
Use global instead of window.
Note that this uses the variable name assigned to the collection object, not the name given to the collection. For this to work with Meteor.users you need to assign another variable name.
if (Meteor.isServer) {
users = Meteor.users;
}
if (Meteor.isClient) {
Meteor.call('count', 'users', function (err, res) {
// do something with number of users
});
}
Also probably a good idea to check that global[collectionName] is actually a collection.
I came up with this code which makes the following assumptions :
collections are declared in the global scope as top level objects.
collections are searched by collection name, not the collection variable identifier.
So client code should declare their collections like this :
MyCollection=new Meteor.Collection("my-collection");
And use the function like this :
var clientResult=Meteor.call("getCollectionCount","my-collection",function(error,result){
if(error){
console.log(error);
return;
}
console.log("actual server-side count is : ",result);
});
console.log("published subset count is : ",clientResult);
The method supports execution on the client (this is known as method stub or method simulation) but will only yield the count of the collection subset replicated client-side, to get the real count wait for server-side response using a callback.
/packages/my-package/lib/my-package.js
getCollection=function(collectionName){
if(collectionName=="users"){
return Meteor.users;
}
var globalScope=Meteor.isClient?window:global;
for(var property in globalScope){
var object=globalScope[property];
if(object instanceof Meteor.Collection && object._name==collectionName){
return object;
}
}
throw Meteor.Error(500,"No collection named "+collectionName);
};
Meteor.methods({
getCollectionCount:function(collectionName){
return getCollection(collectionName).find().count();
}
});
As Meteor.users is not declared as a top level variable you have to account for the special case (yes, this is ugly).
Digging into Meteor's collection handling code could provide a better alternative (getting access to a collection handle by collection name).
Final words on this : using a method call to count a collection documents is unfortunately non-reactive, so given the Meteor paradigm this might be of little use.
Most of the time you will want to fetch the number of documents in a collection for pagination purpose (something like a "Load more" button in a posts list for example), and as the rest of the Meteor architecture you'll want this to be reactive.
To count documents in a collection reactively you'll have to setup a slightly more complicated publication as showcased in the "counts-by-room" example in the docs.
http://docs.meteor.com/#meteor_publish
This is something you definitely want to read and understand.
This smart package is actually doing it right :
http://atmospherejs.com/package/publish-counts
It provides a helper function that is publishing the counts of any cursor.
Keep track of the collections on some other property that the server has access too. You could even call it window if you really wanted to.
var wow = new Meteor.Collection("wow");
collections["wow"] = wow;
getCollectionCount: function (collectionName) {
return collections[collectionName].find().count();
}
If you don't want the package users to change how they work with collections in the app then I think you should use MongoInternals to get collections by name from the db. Not tested but here is an example:
//on server
Meteor.methods({
count: function( name ){
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
var collection = db.collection( name );
return collection && collection.count({});
}
});
Another example of MongoInternals use is here. Documentation of the count() function available from the mongo driver is here.

Is there a post createUser hook in meteor when using accounts-ui package?

Let's say I have a todo app, and I want to make sure that every user that registers has at least one todo to start with, something like "First todo to cross off!", how would I do that in meteor?
In general, the way I see it, I can do it when the user is created for the first time (ideal), or check to see whether they need a new todo every time they log in (less ideal). In the latter case, I can do a check for Todos.findOne(), and if the count is 0, add one. However, seems that whether I do this in my router when the page loads, or on some template's .rendered function, the collection I'm checking hasn't been loaded yet, so I always create a new todo, even if one really does exist. So it'd be great if someone could explain how to get around that.
But, what I'd ideally want is the ability to just create a new Todo when the user is created. There is a Accounts.onCreateUser method, but that is used to add additional info to user profile, not a post-create hook. There's also a method to programmatically create the user using Accounts.createNewUser with a callback, but I'm using the accounts-ui package so am not programmatically adding users. In a less ideal case, I could check for the Todo whenever the user logs in, but even in that case, there seems to be a federated Accounts.loginWithXService login, so not sure how to handle the callback when any user logs in, regardless of service type.
I think I must be missing something simple, so apologies if this is super obvious. Any help is appreciated.
The Meteor API now has the hook onCreateUser:
Accounts.onCreateUser(function (options, user) {
Todos.insert({
owner: user._id,
text: "First todo to cross off!",
});
// We still want the default hook's 'profile' behavior.
if (options.profile)
user.profile = options.profile;
return user;
});
I used the _.wrap method described above but wanted to include an additional suggestion. It's a good idea to call the original callback from your new custom callback. Meteor does some things on the callback that we don't want to miss.
Modified code that worked like a champ for me:
Accounts.createUser = _.wrap(Accounts.createUser, function(createUser) {
// Store the original arguments
var args = _.toArray(arguments).slice(1),
user = args[0];
origCallback = args[1];
var newCallback = function(error) {
// do my stuff
origCallback.call(this, error);
};
createUser(user, newCallback);
});
If you are using the UserAccounts package: postSignUpHook now exists.
Splendido just merged my pull request for exactly this issue.
AccountsTemplates.configure({
/*...*/
postSignUpHook: /*[callback with your actions post full user creation goes here]*/,
/*...*/
}
Documentation (You'll need to scroll down it's the last hook):
func(userId, info) Called, server side only, just after a successfull user account creation, post submitting the pwdForm for sign-up: allows for custom actions on the data being submitted after we are sure a new user was successfully created. A common use might be applying roles to the user, as this is only possible after fully completing user creation in alanning:roles. The userId is available as the first parameter, so that user object may be retrieved. The password is not available as it's already encrypted, though the encrypted password may be found in info if of use.
You can piggyback onto functions that are called by Meteor by wrapping them. I'm also using the accounts-ui and accounts-password packages and I use Underscore's _.wrap method to redefine the loginWithPassword function. Underscore is included in Meteor by default.
I use something like this for logging in:
Meteor.loginWithPassword = _.wrap(Meteor.loginWithPassword, function(login) {
// Store the original arguments
var args = _.toArray(arguments).slice(1),
user = args[0],
pass = args[1],
origCallback = args[2];
// Create a new callback function
// Could also be defined elsewhere outside of this wrapped function
var newCallback = function() { console.info('logged in'); }
// Now call the original login function with
// the original user, pass plus the new callback
login(user, pass, newCallback);
});
In this specific case, the code above would go in your client code somewhere.
For Accounts.createUser, it might look something like this (also somewhere in client code):
Accounts.createUser = _.wrap(Accounts.createUser, function(createUser) {
// Store the original arguments
var args = _.toArray(arguments).slice(1),
user = args[0],
origCallback = args[1];
// Create a new callback function
// Could also be defined elsewhere outside of this wrapped function
// This is called on the client
var newCallback = function(err) {
if (err) {
console.error(err);
} else {
console.info('success');
}
};
// Now call the original create user function with
// the original user object plus the new callback
createUser(user, newCallback);
});
Hope that's helpful.
One of the Meteor devs answered this question in Meteor google group: https://groups.google.com/forum/?fromgroups=#!topic/meteor-talk/KSz7O-tt4w8
Basically, right now, there is no createUser hook when using accounts-ui, only when programmatically doing so via Accounts.createUser. Also, there are no hooks for login, unless using the lower-level login functions like loginWithFacebook, etc. I haven't figured out an ideal way around this yet, but a few ways of handling it:
if needing to enter a default value into a collection, in that collection's subscription, use the onComplete argument. In this callback, if there are no entries in collection, add one. This avoids the first problem I mentioned in my post about not knowing when a collection was loaded, though not ideal since collection could be empty because user already removed first default one:
Meteor.subscribe 'todos', user: Meteor.userId(), () ->
todo = Todos.findOne()
unless todo
Todos.insert user: Meteor.userId()
you can set up a login hook by using the Meteor.autorun reactive method to check for a change in Meteor.userId(). That'll only get called when the user logs in/reloads the page. This is more useful for non-collection stuff since the collection is not guaranteed to be loaded when Meteor.userId is set:
Meteor.autorun () ->
if Meteor.userId()
console.log 'Do some post login hook'
So I think the efficient solution is still out there somewhere, but wanted to update this post with workarounds I had found in the meantime.
I think this answer this question better: How can I create users server side in Meteor?
in resume:
Accounts.createUser({
username: username,
email : email,
password : password,
profile : {
//publicly visible fields like firstname goes here
}
});
check the meteor docs for more: http://docs.meteor.com/#/full/accounts_createuser

Get Meteor collection by name

Suppose I write:
new Meteor.Collection("foos");
new Meteor.Collection("bars");
Is there an API for accessing those collections by name? Something like Meteor.Collection.get(name), where name is "foos" or "bars"? I know I could write something like
var MyCollections = {
foos: new Meteor.Collection("foos");
bars: new Meteor.Collection("bars");
}
and then use MyCollections[name], but I'd prefer to use an existing API if one exists.
Based on Shane Donelley's mongoinspector
https://github.com/shanedonnelly1/mongoinspector
getCollection = function (string) {
for (var globalObject in window) {
if (window[globalObject] instanceof Meteor.Collection) {
if (globalObject === string) {
return (window[globalObject]);
break;
};
}
}
return undefined; // if none of the collections match
};
I've just found that package : https://github.com/dburles/mongo-collection-instances/
It allow you to
Foo1 = new Mongo.Collection('foo'); // local
Foo2 = new Mongo.Collection('foo', { connection: connection });
Mongo.Collection.get('foo') // returns instance of Foo1
Mongo.Collection.get('foo', { connection: connection });
// returns instance of Foo2
Hope it will help
This feature was added to Meteor in Feb 2016: "Provide a way to access collections from stores on the client"
It works like this:
Meteor.connection._stores['tasks']._getCollection();
And I was using it as follows to test inserts using the javascript console:
Meteor.connection._stores['tasks']._getCollection().insert({text:'test'});
For the insert it required the insecure package to still be installed otherwise got an access denied message.
As far as I can see in the collection.js source there currently is no way in the api to get an existing Collection by name, once it has already been initialized on the server. It probably wouldn't be hard to add that feature.
So, why not fork Meteor and submit a patch or create a smart package and share it I'm sure there are others out there who'd like the same feature.
With https://github.com/dburles/mongo-collection-instances you can use Mongo.Collection.get('collectionname')
Note that the parameter you're inserting is the same one you use when creating the collection. So if you're using const Products = new Mongo.Collection('products') then you should use get('products') (lowercase).
Note that they have a return value, so you can just do
var Patterns = new Meteor.Collection("patterns");
and use Patterns everywhere.
And when you need to subscribe to server updates, provide "patterns" to Meteor.subscribe().
If you have the same code for multiple collections, the chance is high that you're doing something wrong from a software engineering viewpoint; why not use a single collection with a type field (or something else that differentiates the documents) and use that instead of using multiple collections?
Rather than looking, I've just been doing:
Foos = new Meteor.Collection("foos");
or possibly put it inside another object. I haven't really been making a Collections collection object.
It seems there is no way to get at the wrapped Meteor.Collection object without saving it at creation time, as others have mentioned.
But there is at least a way to list all created collections, and actually access the corresponding Mongo LocalCollection object. They are available from any Meteor Collection object, so to keep it generalistic you can create a dummy collection just for this. Use a method as such (CoffeeScript):
dummy = new Meteor.Collection 'dummy'
getCollection = (name) ->
dummy._driver.collections[name]
These objects do have all the find, findOne, update et al methods, and even some that Meteor doesn't seem to expose, like pauseObservers and resumeObservers which seem interesting. But I haven't tried fiddling with this mongo LocalCollection reference directly to knowif it will update the server collection accordingly.
var bars = new Meteor.Collection("foos");
Judging by what the collection.js does, the line we use to instantiate the collection object opens a connection to the database and looks for the collection matching the name we give. So in this case a connection is made and the collection 'foos' is bound to the Meteor.Collection object 'bars'. See collection.js AND remote_collection_driver.js within the mongo-livedata package.
As is the way with MongoDB, whilst you can, you don't have to explicitly create collections. As stated in the MongoDB documentation:
A collection is created when the first document is inserted.
So, I think what you're after is what you already have - unless I've totally misunderstood what you're intentions are.
You can always roll your own automatic collection getter.
Say you have a couple of collections called "Businesses" and "Clients". Put a reference each into some "collections" object and register a Handlebars helper to access those "collections" by collections["name"].
i.e. put something like this on the client-side main.js:
collections = collections || {};
collections.Businesses = Businesses;
collections.Clients = Clients;
Handlebars.registerHelper("getCollection", function(coll) {
return collections[coll].find();
});
Then in your HTML, just refer to the collection by name:
{{#each getCollection 'Businesses'}}
<div> Business: {{_id}} </div>
{{/each}}
{{#each getCollection 'Clients'}}
<div> Client: {{_id}} </div>
{{/each}}
Look ma, no more generic "list all records" boilerplate js required!

Resources