This cloud code to make friends worked in Parse.com.
Parse.Cloud.define("friend", function(request, response) {
var userToFriend = new Parse.User();
userToFriend.id = request.params.friendId;
var roleName = "friendsOf_" + request.user.id;
var roleQuery = new Parse.Query("_Role");
roleQuery.equalTo("name", roleName);
roleQuery.first().then(function(role) {
role.getUsers().add(userToFriend);
return role.save();
}).then(function() {
response.success("Success!");
});
});
But this wouldn't work anymore in the cloud code of parse-server, as there is no global current user.
I believe all I have to do to work with session tokens. But I don't quite understand how to get the current user's id.
for example, in order to make the function above work I need to build the roleName with current user id. var roleName = "friendsOf_" + request.user.id;
I believe I need to use the sessionToken to filter for a query that belongs to the current user.
var token = request.user.getSessionToken();
var query = new Parse.Query('???');
query.find({ sessionToken: token }).then( ... );
Can I just do this:
var token = request.user.getSessionToken();
var query = new Parse.Query('User');
query.find({ sessionToken: token }).then( function(user) {
var roleName = "friendsOf_" + user.id;
} );
Any advice would be appreciated,
Related
i'm not a programmer. i'm just trying to clean my gmail account and i figured i could put chatgpt to use. it came up with this code, but i'm getting an error when i run it in apps script: hoping someone can shed some light on what's wrong with it.
thanks
function countEmailsBySender() {
// Replace the following variables with your own values
var maxEmails = 4000; // the maximum number of emails to process
var spreadsheetId = 'YOUR_SPREADSHEET_ID'; // the ID of your Google Spreadsheet
var sheetName = 'Emails By Sender'; // the name of the sheet to create in your Google Spreadsheet
var clientId = 'YOUR_CLIENT_ID'; // your OAuth 2.0 client ID
var clientSecret = 'YOUR_CLIENT_SECRET'; // your OAuth 2.0 client secret
// Create a new sheet in the specified spreadsheet
var sheet = SpreadsheetApp.openById(spreadsheetId).insertSheet(sheetName);
// Get the authorization token
var service = getGmailService(clientId, clientSecret);
if (!service.hasAccess()) {
Logger.log(service.getAuthorizationUrl());
throw new Error('Authorization required');
}
// Get the list of messages
var query = 'is:inbox';
var threads = Gmail.Users.Threads.list('me', { q: query, maxResults: maxEmails }, { auth: service }).threads;
// Count the number of emails sent by each sender
var emailCount = {};
threads.forEach(function(thread) {
var messages = Gmail.Users.Messages.get('me', thread.messages[0].id, { auth: service }).payload.headers;
var sender = messages.find(header => header.name === 'From').value;
emailCount[sender] = (emailCount[sender] || 0) + 1;
});
// Sort the senders by the number of emails they sent
var senders = Object.keys(emailCount).sort(function(a, b) {
return emailCount[b] - emailCount[a];
});
// Output the senders and the number of emails they sent to the spreadsheet
sheet.appendRow(['Sender', 'Email Count']);
senders.forEach(function(sender) {
sheet.appendRow([sender, emailCount[sender]]);
});
Logger.log('Done.');
}
function getGmailService(clientId, clientSecret) {
return OAuth2.createService('gmail')
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setClientId(clientId)
.setClientSecret(clientSecret)
.setScope('https://www.googleapis.com/auth/gmail.readonly')
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
.setCache(CacheService.getUserCache())
.setParam('access_type', 'offline')
.setParam('approval_prompt', 'force');
}
function authCallback(request) {
var service = getGmailService();
var authorized = service.handleCallback(request);
if (authorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Access denied.');
}
}
i get an error (ReferenceError: OAuth2 is not defined) on these lines :
return OAuth2.createService('gmail')
var service = getGmailService(clientId, clientSecret);
am i missing something?
thanks in advance
haven't tried anything else
I have implemented google login in parse. Here is my code:
var querystring = require('querystring');
var _ = require('underscore');
var Buffer = require('buffer').Buffer;
var googleValidateEndpoint = 'https://www.googleapis.com/oauth2/v1/userinfo';
var TokenStorage = Parse.Object.extend("TokenStorage");
var restrictedAcl = new Parse.ACL();
restrictedAcl.setPublicReadAccess(false);
restrictedAcl.setPublicWriteAccess(false);
Parse.Cloud.define('accessGoogleUser', function(req, res) {
var data = req.params;
var token = data.code;
/**
* Validate that code and state have been passed in as query parameters.
* Render an error page if this is invalid.
*/
if (!(data && data.code)) {
res.error('Invalid auth response received.');
return;
}
Parse.Cloud.useMasterKey();
Parse.Promise.as().then(function() {
// Validate & Exchange the code parameter for an access token from Google
return getGoogleAccessToken(data.code);
}).then(function(httpResponse) {
var userData = httpResponse.data;
if (userData && userData.id) {
return upsertGoogleUser(token, userData, data.email);
} else {
return Parse.Promise.error("Unable to parse Google data");
}
}).then(function(user) {
/**
* Send back the session token in the response to be used with 'become/becomeInBackground' functions
*/
res.success(user.getSessionToken());
}, function(error) {
/**
* If the error is an object error (e.g. from a Parse function) convert it
* to a string for display to the user.
*/
if (error && error.code && error.error) {
error = error.code + ' ' + error.error;
}
res.error(JSON.stringify(error));
});
});
var getGoogleAccessToken = function(code) {
var body = querystring.stringify({
access_token: code
});
return Parse.Cloud.httpRequest({
url: googleValidateEndpoint + '?access_token=' + code
});
}
var upsertGoogleUser = function(accessToken, googleData, emailId) {
var query = new Parse.Query(TokenStorage);
query.equalTo('accountId', googleData.id);
//query.ascending('createdAt');
// Check if this googleId has previously logged in, using the master key
return query.first({ useMasterKey: true }).then(function(tokenData) {
// If not, create a new user.
if (!tokenData) {
return newGoogleUser(accessToken, googleData, emailId);
}
// If found, fetch the user.
var user = tokenData.get('user');
return user.fetch({ useMasterKey: true }).then(function(user) {
// Update the access_token if it is different.
if (accessToken !== tokenData.get('accessToken')) {
tokenData.set('accessToken', accessToken);
}
/**
* This save will not use an API request if the token was not changed.
* e.g. when a new user is created and upsert is called again.
*/
return tokenData.save(null, { useMasterKey: true });
}).then(function(obj) {
// Reset password
password = new Buffer(24);
_.times(24, function(i) {
password.set(i, _.random(0, 255));
});
password = password.toString('base64')
user.setPassword(password);
return user.save();
}).then(function(user) {
// ReLogin
// This line is what I am talking about
return Parse.User.logIn(user.get('username'), password);
}).then(function(obj) {
// Return the user object.
return Parse.Promise.as(obj);
});
});
}
var newGoogleUser = function(accessToken, googleData, email) {
var user = new Parse.User();
// Generate a random username and password.
var username = new Buffer(24);
var password = new Buffer(24);
_.times(24, function(i) {
username.set(i, _.random(0, 255));
password.set(i, _.random(0, 255));
});
var name = googleData.name;
// name = name.split(" ");
// var fullname = name;
// if(name.length > 1)
// var lastName = name[name.length-1];
user.set("username", username.toString('base64'));
user.set("password", password.toString('base64'));
user.set("email", email);
user.set("fullName", name);
// user.set("last_name", lastName);
user.set("accountType", 'google');
// Sign up the new User
return user.signUp().then(function(user) {
// create a new TokenStorage object to store the user+Google association.
var ts = new TokenStorage();
ts.set('user', user);
ts.set('accountId', googleData.id);
ts.set('accessToken', accessToken);
ts.setACL(restrictedAcl);
// Use the master key because TokenStorage objects should be protected.
return ts.save(null, { useMasterKey: true });
}).then(function(tokenStorage) {
return upsertGoogleUser(accessToken, googleData);
});
}
It works perfectly fine. Now the problem I am facing is that I want to link google account with an existing parse account created using email or username & password. The problem in doing so is that to login/signup using google I have to reset the password of the user to login so as to get the session token. See this line in the code -> [This line is what I am talking about]. So if I do so an existing user who had earlier used username/email & password to login won't be able to login again using email since I have reset his/her password. I have seen this and all the other links related to this but none of which solves this problem.
Can somebody here guide me in the right direction?
Log added as response to one of the comments:
{"accountType":"google","createdAt":"2016-01-07T17:30:57.429Z","email":"skdkaney#gmail.com","fullName":"ashdakhs basdkbney","updatedAt":"2016-01-07T17:30:57.429Z","username":"owt3h0ZZEZQ1K7if55W2oo3TBLfeWM6m","objectId":"lSlsdsZ9"}
Added upsert function as per comment request:
var upsertGoogleUser = function(accessToken, googleData, emailId) {
var query = new Parse.Query(TokenStorage);
query.equalTo('accountId', googleData.id);
//query.ascending('createdAt');
// Check if this googleId has previously logged in, using the master key
return query.first({ useMasterKey: true }).then(function(tokenData) {
// If not, create a new user.
if (!tokenData) {
return newGoogleUser(accessToken, googleData, emailId);
}
// If found, fetch the user.
var userw = tokenData.get('user');
var users_id = userw.id;
var query2 = new Parse.Query(Parse.User);
query2.equalTo('objectId',users_id);
// The new query added
return query2.first({ useMasterKey: true }).then(function(user) {
// Update the access_token if it is different.
// if (accessToken !== tokenData.get('accessToken')) {
// tokenData.set('accessToken', accessToken);
// }
console.log(user);
console.log("******");
/**
* This save will not use an API request if the token was not changed.
* e.g. when a new user is created and upsert is called again.
*/
// return tokenData.save(null, { useMasterKey: true });
}).then(function(obj) {
console.log(obj);
// console.log(user);
var result = user ;
// Return the user object.
return Parse.Promise.as(result); // this is the user object acquired above
});
After a discussion with OP, there are possible solutions to this matter but each of them have pros and cons.
Disabling Revocable Session
Since the introduction of Revocable Session, getSessionToken will always return undefined even with master key. To turn it off, go to App Settings >> Users >> Turn off Require revocable sessions.
Then, in upsertGoogleUser method, you just need to return the user object from tokenData.get('user'). It is enough to call user.getSessionToken() in your main cloud function. The final method should look like:
var upsertGoogleUser = function(accessToken, googleData, emailId) {
Parse.Cloud.useMasterKey();
var query = new Parse.Query(TokenStorage);
query.equalTo('accountId', googleData.id);
//query.ascending('createdAt');
// Check if this googleId has previously logged in, using the master key
return query.first().then(function(tokenData) {
// If not, create a new user.
if (!tokenData) {
return newGoogleUser(accessToken, googleData, emailId);
}
// If found, fetch the user.
var userw = tokenData.get('user');
var users_id = userw.id;
var query2 = new Parse.Query(Parse.User);
query2.equalTo('objectId',users_id);
return query2.first().then(function(user) {
console.log(user);
console.log(user.getSessionToken());
console.log("******");
return Parse.Promise.as(user);
});
});
};
User Password Input
In order not to change user's password, we can ask user to input his password once we successfully authenticated Google data. We then use the input password to log user in. This is not a good UX, since the purpose of Google Login is to increase usability by letting users not entering password.
Query on Parse.Session
This is a possible solution if you want to use "Revocable Session" feature. In the code above, instead of querying on Parse.User, we can look for any revocable session in Parse.Session class. Then we can call getSessionToken on returned object. This is not optimal solution in cases that we need to know which devices the user is logged in.
Reference:
Parse's Enhanced Session: http://blog.parse.com/announcements/announcing-new-enhanced-sessions/
Hi i am creating an empty array then populating it with data from a mongo query using a forEach Loop.
i have been attempting this now for 4 days and nothing i seem to do is working i know im close but being a newbie to javascript and MEAN stack i just cant figure it out.
i have attached the code with comments on everything i am trying to do.
please any help would be awesome..
var mongoose = require('mongoose'),
User = require('../../models/UserModel'),
async = require('async');
module.exports.getfollowing = function(req, res){
//grab the Users ID from the body
var thefollower = req.body.follower;
//create empty array that i want to populate with the followee's ID and Avatar url
var obj = [];
//query mongo for the user
User.findOne({ _id: thefollower }, function (err, user) {
if (err) {
console.log(err);
res.json(err);
} else {
//grab the following element of the users mongo schema -- should return all the followee's ID's -- tested works
var following = user.following;
//iritate throught all the followee's
async.forEach(following, function(item, callback) {
//current followee
var user = item;
//query mongo for the followee
User.findOne({_id: user}, function(err, followee, callback){
//get followee's ID and Avatar url
var id = followee._id;
var avatar = followee.avatar;
//add the followee's ID and Avatar url to the obj array
obj.push({
id: id,
avatar: avatar
});
});
//see if this worked - returns empty
console.log(obj);
callback();
}, function(err) {
//see if this worked - returns empty
console.log(obj);
//respond to the client - returns empty
res.json(obj);
});
}
});
};
You need to move your callback(); that is at the end of your async.forEach() callback to inside the User.findOne({_id: user}, ...) callback (right after you call obj.push()) because that is when you are actually done with item. With your current code you are telling the async module immediately that you are done with item, before your mongo query has a chance to complete.
mscdex
Was spot on his answer solved my issue for future help to others here is the code
var mongoose = require('mongoose'),
User = require('../../models/UserModel'),
async = require('async');
module.exports.getfollowing = function(req, res){
//grab the Users ID from the body
var thefollower = req.body.follower;
//create empty array that i want to populate with the followee's ID and Avatar url
var obj = [];
//query mongo for the user
User.findOne({ _id: thefollower }, function (err, user) {
if (err) {
console.log(err);
res.json(err);
} else {
//grab the following element of the users mongo schema -- should return all the followee's ID's -- tested works
var following = user.following;
//iritate throught all the followee's
async.forEach(following, function(item, callback) {
//current followee
var user = item;
//query mongo for the followee
User.findOne({_id: user}, function(err, followee){
//get followee's ID and Avatar url
var id = followee._id;
var avatar = followee.avatar;
//add the followee's ID and Avatar url to the obj array
obj.push({
id: id,
avatar: avatar
});
//see if this worked - returns empty
console.log(obj);
callback();
});
}, function(err) {
//see if this worked - returns empty
console.log(obj);
//respond to the client - returns empty
res.json(obj);
});
}
});
};
Sorry for the long post, but I'm not sure the issue comes across without all the code.
I'm using parse.com and the JavaScript SDK.
The below code is part of my users profile page, it displays their profile picture to them on screen and allows them to change it.
Changing the profile picture works fine and is uploaded as expected. The issue is that on the profile page the profile is taking the first users picture and displaying it on page for any user. Basically its not looking at the current user.
I think that I need to update the query to include something like var currentUser = Parse.User.current(); and introduce it into my code. Whatever why I try and do this I'm hitting a brick wall. Any help would be great
I'm struggling if this is the case to understand how to change my code to avoid this happening?
/////////////////////////Queries the users profile picture and shows on page////////////////////////
var UserProfilePic = Parse.Object.extend("_User");
var query = new Parse.Query(UserProfilePic);
query.exists("ProfilePic");
query.find({
success: function(results) {
// If the query is successful, store each image URL in an array of image URL's
imageURLs = [];
for (var i = 0; i < results.length; i++) {
var object = results[i];
imageURLs.push(object.get('ProfilePic').url());
}
// If the imageURLs array has items in it, set the src of an IMG element to the first URL in the array
if (imageURLs.length > 0) {
$('#profile_pic').attr('src', imageURLs[0]);
}
$('#Image01').attr('src', imageURLs[0]); //first image
},
error: function(error) {
// If the query is unsuccessful, report any errors
alert("Error: " + error.code + " " + error.message);
}
});
///////// Saves the users profile image and fields after the #save button is clicked//////
var profileSave = Parse.User.current();
function ProfileSave() {
var User = Parse.Object.extend("_User");
var profileSave = Parse.User.current();
var saveusername = $('#username').val();
var saveemail = $('#email').val();
var savegender = $('#gender').val();
profileSave.set("username", saveusername);
profileSave.set("email", saveemail);
profileSave.set("gender", savegender);
profileSave.save(null, {
success: function(profileSave) {
profileSave.save();
console.log("DONE");
alert("Profile Saved");
},
error: function(profileSave, error) {
// Fail
}
});
}
///////////////Allows the user to upload a profile image and store///////////////////////
$(document).ready(function() {
var parseAPPID = "XXXXXX";
var parseJSID = "XXXXXX";
//Initialize Parse
Parse.initialize(parseAPPID, parseJSID);
$("#fileUploadBtn").on("click", function(e) {
var fileUploadControl = $("#fileUploader")[0];
if (fileUploadControl.files.length > 0) {
var file = fileUploadControl.files[0];
var name = file.name;
console.log("here goes nothing...");
var parseFile = new Parse.File(name, file);
parseFile.save().then(function() {
console.log("Worked!");
console.dir(arguments);
var User = Parse.Object.extend("_User");
var jobApplication = Parse.User.current();
jobApplication.set("ProfilePic", parseFile);
jobApplication.save();
var profilePhoto = jobApplication.get("ProfilePic");
console.log("Done");
//jobApplication.get("ProfilePic").url;
}, function(error) {
console.log("Error");
console.dir(error);
});
}
});
});
Just found this in the featured/bounties, even though I answered it here for you, so here's my copy/pasted answer:
You're not using query.exists() the way you think you are. Also, are you just trying to check to see if the current user has a profile image? Because you can just use the .has("key") method of objects, and a user is an object. If this is in cloud code, rather than client code, you may have to fetch the user first. If it is client code, you should already have an up to date user.
You should not be extending the user object. Not necessary at all. Use the Parse.User object type, so if you're querying for users, do var query = new Parse.Query( Parse.User );
So I think what you want is something more like:
var user = Parse.User.current();
if( user.has("ProfilePic") )
{
//Do your stuff with the image
var imageURL = user.get("ProfilePic").url();
$('#Image01').attr('src', imageURL);
}
else
{
alert("Error: User does not have a 'ProfilePic' set");
}
The way your code is set up, you're just querying through all the users with profilePics and taking the first one. My code only gets the profilePic of the current user.
I am writing a script on parse.com's javascript cloud code SDK. Here is the information I have saved in my parse.com account and what I am trying to do with it.
I have a bunch of items saved in a parse class called TestItem, theses items have an objectId, item name, meal time (lunch, dinner) and a location for there columns. I also have a class called UserFavourites. In this class the objects have an objectId, item name and a pointer to the user who saved the item as a favourite.
And with this information I am trying to write a cloud code script in javascript. That will match the an item(s) to the item(s) that a user has favourited and send them a push notification saying where and what the item is and the location of the item. I have some code that will do that but this code will send a different notification for each item which could get annoying for the user here is that code.
Parse.Cloud.define("push", function(request, response) {
var TestItem = Parse.Object.extend("TestItem");
var query = new Parse.Query(TestItem);
query.limit(1000);
query.equalTo('school', 'Union College (NY)');
query.find({
success: function(resultsItem) {
//console.log("Successfully retrieved " + resultsItem.length + " :1111.");
for (var i = 0; i < resultsItem.length; i++) {
var object = resultsItem[i];
var item = object.get('item');
var school = object.get('school');
var meal = object.get('meal');
var meal = meal.toLowerCase();
var diningLocation = object.get('schoolMenu');
//var itemArray = [];
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryFavourite = new Parse.Query(UserFavourite);
queryFavourite.limit(1000);
queryFavourite.equalTo("item", item)
queryFavourite.equalTo("school", school)
queryFavourite.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var objectFav = results[i];
var user = objectFav.get('user');
var userID = user.id;
var realItem = objectFav.get('item');
console.log(objectFav.get('user'));
console.log(objectFav.get('item'));
var UserClass = Parse.Object.extend("User");
var queryUser = new Parse.Query(UserClass);
queryUser.get(userID, {
success: function(userResult) {
console.log(userResult.get('school'));
console.log('install:' + userResult.get('installation').id);
var userInstallationId = userResult.get('installation').id;
var queryInstallation = new Parse.Query(Parse.Installation);
queryInstallation.equalTo('objectId', userInstallationId);
queryInstallation.find({
success: function(results) {
console.log('number' + results.length);
Parse.Push.send({
// deviceType: [ "ios" ],
where: queryInstallation,
data: {
alert: realItem + " is being served at " + diningLocation + " for " + meal
}
},
{
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
},
error: function(error) {
console.log('error');
}
});
},
error: function(error) {
console.log('error');
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
});
As you can see it is quite long and not very nice looking, I tried to save items to an array so to avoid sending two or more notifications but couldn't get that to work.
So I started writing another script that uses promises which looks much nicer but haven't gotten it all the way right now, it can match the items to users that have an item favourited and put the objectId's of those users in an array. Here is that code.
Parse.Cloud.define("test", function(request, response) {
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryFavourite = new Parse.Query(UserFavourite);
var userArray = [];
var TestItem = Parse.Object.extend("TestItem");
var query = new Parse.Query(TestItem);
query.limit(1000);
query.equalTo('school', 'Union College (NY)');
query.find().then(function(results) {
return results;
}).then(function(results) {
var promises = [];
for (var i = 0; i < results.length; i++) {
var object = results[i];
var item = object.get('item');
var school = object.get('school');
var meal = object.get('meal');
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryUser = new Parse.Query(UserFavourite);
queryUser.equalTo("item", item);
queryUser.equalTo("school", school);
var prom = queryUser.find().then(function(users) {
for (var i = 0; i < users.length; i++) {
var user = users[i];
var userID = user.get('user').id;
if (userArray.indexOf(userID) === -1) {
userArray.push(userID);
}
}
return userArray;
});
promises.push(prom);
}
return Parse.Promise.when.apply(Parse.Promise, promises);
}).then(function(results) {
console.log(userArray);
});
});
But now with this code I don't know where to go, I think using promises and such is the right way to go but I am now confused as once I have all the users that have an item favourited what to do, I then need to get there items that are favourited and are available in the TestItem class, this is where I am struggling.
Here is a pic of my UserFavourite class it has a pointer to the user who favorited the item as you can see, and also a user has more than one favorite.
Thanks a bunch for the help in advance.
Here is your code, and I changed a couple things.
Parse.Cloud.define("getAllFavoriteItems", function (request, response) {
var TestItems = Parse.Object.extend("TestItems");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItems);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', 'Union College (NY)');
userFavoritesQuery.include('testItems'); //This makes sure to pull all of the favorite item data instead of just the pointer object
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.limit(1000); //limit results to 1000
userFavoritesQuery.ascending('userId'); //group the user id's together in your array
userFavoritesQuery.find({
success:function(results) {
var pushNotificationMessage = "";
var userId = "";
for (var i=0; i <results.length; i++) {
if (results[i].get('userId') != userId) {
if (results[i].get('userId') != "") {
//TODO send push notification
}
userId = results[i].get('userId');
pushNotificationMessage = ""; //start a new push notification
}
pushNotificationMessage += results[i].get('item').get('name') + ": " + results[i].get('item').get('location') + "\n";
//SOMEWHERE BEFORE HERE I NEED THE INSTALLATION ID OF THE USER
//TO SEND THE PUSH TO THAT USER
Parse.Push.send({
// deviceType: [ "ios" ],
where: queryInstallation,
data: {
alert: pushNotificationMessage
}
},
{
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
}
response.success(true);
},
error:function(error) {
response.error();
}
})
});
Some code that might create push per user, rough outline though
if (i > 0) {
if (results[i].get('user') === results[i-1].get('user')) {
userItems.push(results[i]);
}
else {
userItems.length = 0;
}
}
else {
userItems.push(results[i]);
}
Not sure let me know if you understand what I'm trying to do...
So it a user has two items favourited I want it to group that into one, phrase that says what and where both items are being served
And here is code to send push
Parse.Push.send({
// deviceType: [ "ios" ],
where: queryInstallation,
data: {
alert: pushNotificationMessage
}
},
{
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
It can also be done with then/ promises,
I agree with #Maxwell that your UserFavorite should have links to both User and TestItem. This makes it possible to make your cloud-function as simple as:
Parse.Cloud.define("getAllFavoriteItems", function(request, response) {
var TestItem = Parse.Object.extend("TestItem");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItem);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', request.params.school);
userFavoritesQuery.include('testItem');
userFavoritesQuery.include('user');
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.find().then(function(results) {
var alerts = {};
for(var i =0 ; i<results.length; i++ ){
var user = results[i].get('user');
var testItem = results[i].get('testItem');
if(user && testItem){
var instId = user.get('installationId');
if(!alerts[instId]) {
alerts[instId] = [];
}
var m = results[i].get('item') + " is being served at {{diningLocation}} for " + testItem.get('meal');
alerts[instId].push(m);
}
}
response.success(alerts);
}, function(error) {
response.error();
});
});
This is working code that you can also find in my github repo.
You can also see the working demo here
The idea is the same as in Maxwell's answer: to have link in UserFavorites class to both User (where installationId is located) and TestItem entities. I've just made it working by including user and testItems properties in query, so when the result is returned filtered by school name I already have a list of installationIds.
Here is my schema:
User
TestItem
UserFavorites
Update:
In this code I added push notifications:
Parse.Cloud.define("getAllFavoriteItems", function(request, response) {
var TestItem = Parse.Object.extend("TestItem");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItem);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', request.params.school);
function SendPush(installationId, msg) {
var query = new Parse.Query(Parse.Installation);
query.equalTo('objectId', installationId);
Parse.Push.send({
where: query,
data: {alert: msg}
});
}
userFavoritesQuery.include('testItem');
userFavoritesQuery.include('user');
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.find().then(function(results) {
var groupedAlerts = {};
// manually iterating though results to get alert strings ang group by user in groupedAlerts[installationId]
for(var i =0 ; i<results.length; i++ ){
var user = results[i].get('user');
var testItem = results[i].get('testItem');
if(user && testItem){
var instId = user.get('installationId');
if(!groupedAlerts[instId]) {
groupedAlerts[instId] = [];
}
var m = results[i].get('item') + " is being served at {{dining Location}} for " + testItem.get('meal');
groupedAlerts[instId].push(m);
}
}
// reformat to array and send push notifications
var alerts = [];
for(var key in groupedAlerts) {
alerts.push({
installationId: key,
alerts: groupedAlerts[key],
});
// Send push notifications
SendPush(key, groupedAlerts[key].join());
}
response.success(alerts);
}, function(error) {
response.error();
});
});
I've also updated test data in live demo (just press Get Alerts) or feel free to play around with test data hot it changes cloud code response. gitnub repo is also up to up to date.
This is based on what I understand as the problem you're trying to solve. If it's not addressing the right issue, let me know and I'll see what I can do.
Looking first at your database model, we can simplify this a bit by modifying the UserFavorites table. Starting with the initial two classes, you have a table of items and a table of users. Since a user can favorite many items and an item can be favorited by many users, we have a many-to-many relationship that exists. When this happens, we need to make a third class that points to each of the other two classes. This is where the UserFavorites table comes into play. In Parse terms, the UserFavorites table needs to have two pointers in it: one for the user and one for the item.
Once the UserFavorite table exists with it's two pointers, we can do a few things fairly easily. In your case, we have a few searching criteria:
each item must be at a given school
you want to limit your responses to the first 1000
To accomplish this you can combine two queries into one by calling matchesQuery.
Parse.Cloud.define("getAllFavoriteItems", function (request, response) {
var TestItems = Parse.Object.extend("TestItems");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItems);
var userQuery = new Parse.Query(Parse.User);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', 'Union College (NY)');
userQuery.include('Installation');
userFavoritesQuery.include('testItems'); //This makes sure to pull all of the favorite item data instead of just the pointer object
userFavoritesQuery.include('User'); //This makes sure to pull all of the favorite item data instead of just the pointer object
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.matchesQuery('user', userQuery); //This will run the third query against Users, bringing the installation data along with it
userFavoritesQuery.limit(1000); //limit results to 1000
userFavoritesQuery.ascending('userId'); //group the user id's together in your array
userFavoritesQuery.find({
success:function(results) {
...
},
error:function(error) {
response.error();
}
})
})
Once we get that far, then compiling the push message for each user should be a matter of straight-forward string parsing logic. For example, in the success function, one way we can extract the data we is this:
success:function(results) {
var pushNotificationMessage = "";
var userId = "";
for (var i=0; i <results.length; i++) {
if (results[i].get('userId') != userId) {
if (results[i].get('userId') != "") {
//TODO send push notification
}
userId = results[i].get('userId');
pushNotificationMessage = ""; //start a new push notification
}
pushNotificationMessage += results[i].get('item').get('name') + ": " + results[i].get('item').get('location') + "\n";
}
response.success(true);
}
I haven't tested these examples to see if they'll work, but I hope this gives you an idea of how to simplify your queries into something a little more manageable.