Sharing client variable with Meteor.onCreateUser function on server - javascript

I would like to share a variable that is set in the client with the Meteor.onCreateUser function call on the server.
I have this code that sets some user properties before a user is created
Accounts.onCreateUser(function(options, user, err) {
if (options.profile) {
user.profile = options.profile;
// Images
var picturelrg = "http://graph.facebook.com/" + user.services.facebook.id + "/picture/?type=large";
var picturesm = "http://graph.facebook.com/" + user.services.facebook.id + "/picture/?type=small";
options.profile.picturelrg = picturelrg;
options.profile.picturesm = picturesm;
options.profile.upvotes = 0;
options.profile.neutralvotes = 0;
options.profile.downvotes = 0;
// ip = response.ip;
return user;
}
});
Here is the client code
if (Meteor.isClient) {
fbLogin = function() {
Meteor.loginWithFacebook({
requestPermissions: ['public_profile', 'email', 'user_location']
}, function(err) {
if (err)
// redirect to register if popup comes and user isn't on register
Session.set('errorMessage', err.reason || 'Unknown Eror');
console.log(Session.get('errorMessage'));
});
}
locate = function(){
function ipLocate(whenDone) {
var api = "http://ipinfo.io?callback=?";
$.getJSON(api, {
format: "jsonp"
})
.done(function(response) {
var result = ""
// show all the props returned
for (var prop in response) {
result += prop + ": " + response[prop] + "<br>";
}
var selectedResponse = {
city: response.city,
region: response.region,
country: response.country,
ip: response.ip,
latLng: response.loc
}
console.log(selectedResponse);
whenDone(selectedResponse);
return selectedResponse
});
}
// HACK: Async
function ipDone(selectedResponse) {
response = selectedResponse;
}
// Set response
ipLocate(ipDone);
return response
}
Template.ModalJoin.events({
'click .modJoinFB-Btn ': function() {
locate();
fbLogin();
}
});
}
On the client I have an event handler that sets some values when the user clicks the "Sign Up with Facebook" button. How can I send these values to the onCreateUser function to be accessed.
Ex: I want to store user geolocation info ( city, state) when the user registers but I don't know how this can be sent from the client to server.
I'm not sure how I would use Meteor.call() if I could

Looks like you should run a Meteor.call function inside fbLogin, passing that location data, if no error is returned. Something like this:
fbLogin = function() {
Meteor.loginWithFacebook({
requestPermissions: ['public_profile', 'email', 'user_location']
}, function(err) {
if (err) {
Session.set('errorMessage', err.reason || 'Unknown Eror');
console.log(Session.get('errorMessage'));
} else {
//if no error was returned, then Meteor.call the location
var userId = Meteor.userId(); //you should send that userId for the method.
Meteor.call('storeLocation', locationData, userId, function(err,res){
if (err) {
console.log(err);
}
});
}
});
}
And on server, you create a Method for updating that user profile data with the location. Maybe something like this:
Meteor.methods({
'storeLocation': function(locationData, userId) {
var locationData = {
// based on what you have gathered on your client location function
'city': response.city,
'region': response.region,
'country': response.country,
'ip': response.ip,
'latLng': response.loc
}
Meteor.users.update(
//I suggest placing it inside profile, but do as it is better to you
{'_id': userId},
{$addToSet: {'profile.locations': locationData }}
);
}
});
Not sure if you will store like that, but this is how I have done for myself. Let me know if any problems or doubts, we can try to solve it together.

Related

Parse Cloud code not responding error or success with promises

My Parse cloud code is not responding with an error or success. It just times out, I don't know what I'm doing wrong. It should save multiple values in different tables, and finish with sending a push message to a user.
The push message and user table are adjusted and send, but the street and the ledger objects are not being saved correctly. Also the response is not being called.
I did work in the past (or I was just lucky). Any thought on what I'm doing wrong?
var buyerId = request.params.buyerid;
var sellerName = request.params.sellername;
var streetId = request.params.streetid;
var amount = request.params.amount;
var pushMessageTemplate = request.params.pushMessage;
var log = request ? request.log : console;
var Streets = Parse.Object.extend('SHStreets');
var streetQuery = new Parse.Query(Streets);
streetQuery.equalTo("objectId", streetId);
streetQuery.first({
useMasterKey: true,
success: function (street) {
var streetName = street.get("name");
var query = new Parse.Query(Parse.User);
query.equalTo("objectId", buyerId);
query.first({
useMasterKey: true,
success: function (user) {
var promises = [];
var now = new Date();
var buyerName = user.get("username");
// Set and save the change
user.set("balance", user.get("balance") - amount);
user.set("streets", user.get("streets") + 1);
street.set("current_owner", user);
street.set("owned_since", now);
street.set("for_sale", false);
street.set("price_bought", amount);
var acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setWriteAccess(user, true);
street.setACL(acl);
//update ledger
var Ledger = Parse.Object.extend("Ledger");
var ledger = new Ledger();
ledger.set("type", "buy");
ledger.set("amount", amount);
ledger.set('ledger_time', now);
ledger.set("user", user);
ledger.set("description", "x");
promises.push(Parse.Object.saveAll([street, user, ledger], { useMasterKey: true }));
// Find users with a given email
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo("objectId", user.id);
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.exists("user"); // filter out installations without users
pushQuery.include('user'); // expand the user pointer
pushQuery.matchesQuery("user", userQuery);
// Send push notification to query
promises.push(Parse.Push.send({
where: pushQuery, // Set our installation query
data: {
alert: _.template(pushMessageTemplate)({
sellername: sellerName,
streetname: streetName,
amount: amount
})
}
}, {
useMasterKey: true,
}));
return Parse.Promise.when(promises).then(function () {
response.success("success");
});
},
error: function (error) {
log.error('buy-street error', error.message);
response.error("Uh oh, buy request success failed." + error.message);
}
});
},
error: function (error) {
log.error('buy-street error', error.message);
response.error("Uh oh, buy request success failed." + error.message);
}
});
It looks like your first query's success function doesn't return anything:
streetQuery.first({
useMasterKey: true,
success: function (street) { // this function doesn't return anything
// ...
query.first({ // You probably want to return this promise
useMasterKey: true,
success: function (user) { // this is the function that you're returning to
// ...
return Parse.Promise.when(promises).then(function () {
response.success("success");
});
},
});
}
});
Javascript will return undefined by default if you don't have a return statement.
I was missing a function error for the return of the promises.
return Parse.Promise.when(promises).then(function () {
// Set the job's sucess status
response.success('success');
},function(error) {
response.error(error);
});

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,

Parse cloud code error

I tried setting up cloud code on my new parse server (on AWS EB) to send push notifications with the following code:
Parse.Cloud.define("sendPush", function(request, response) {
var currentUser = request.user;
var recipient = request.params.recipient;
if (!currentUser) {
response.error("Must be logged in.");
return;
}
if (!recipient) {
response.error("Must specify recipient.");
return;
}
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('objectId', recipient);
response.success(userQuery.get('name'))
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.matchesQuery('user', userQuery);
Parse.Push.send({
where: pushQuery,
data: {
alert: "Hello, World",
badge: "Increment"
}
}, {
success: function() {
response.success("Push sent to " + recipient + " from " + request.user.get("username"));
},
error: function(error) {
response.error('Not working')
}
});
});
It doesn't return back an error but the notification doesn't go through either. Instead, I get the following error:
Optional({
"_rejected" = 0;
"_rejectedCallbacks" = (
);
"_resolved" = 0;
"_resolvedCallbacks" = (
);
})
Any idea why this is happening?
EDIT
I decided to change the code to see if the function is even retrieving results, but looks like it doesn't. Here's the new code:
Parse.Cloud.define("sendPush", function(request, response) {
var currentUser = request.user;
var recipient = request.params.recipient;
if (!currentUser) {
response.error("Must be logged in.");
return;
}
if (!recipient) {
response.error("Must specify recipient.");
return;
}
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('objectId', recipient);
userQuery.find
({
success: function(results)
{
if (results.length > 0)
{
var objectId = results[0].id;
var gate = results[0].get("name");
response.success(JSON.stringify(results[0]));
}
else
{
response.error("wont work");
};
}
})
});
and get the following error:
Optional(Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (No value.) UserInfo=0x7ff163c587d0 {NSDebugDescription=No value.})

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?

stripe.js: api call that calls stripe.charges.create isn't returning a response

I'm trying to use stripe.js and return the created charge after saving it on the server. The stripe charge saves successfully and is saved in my stripe test account, but it seems like the callback isn't working. From my code below, the console.log('success') gets called, but the "then" callback isn't called, and the console.log('savedChanges') doesn't get called either. Am I doing something wrong? I was following https://stripe.com/docs/tutorials/charges for reference.
/app/scripts/controllers/stripe.js
'use strict';
app.controller('StripeCtrl', function ($scope, $location, $http) {
$scope.charge = {};
$scope.successfulCharge = null;
$scope.submitCharge = function(status, response){
var $form = $('#payment-form');
if (response.error) {
$form.find('.payment-errors').text(response.error.message);
$form.find('button').prop('disabled', false);
} else {
var token = response.id;
var data = {
amount: ($scope.charge.amount * 100),
card: token,
description: "2014 missions donation for " + $scope.charge.missionary,
metadata: {
'email': $scope.charge.email,
'missionary': $scope.charge.missionary
}
};
$http.post('/api/stripe/submitCharge', data).success(function(data, status, headers) {
console.log('submitCharge success!!!');
console.log(data);
$scope.successfulCharge = data;
});
}
}
$scope.getCharges = function(){
$http.get('/api/charges').success(function(charges) {
return charges;
});
}
});
/lib/controllers/api.js
'use strict';
var mongoose = require('mongoose'),
config = require('../config/config'),
stripe = require('stripe')(config.stripe.secret_key),
Charge = mongoose.model('Charge');
exports.charges = function(req, res) {
return Charge.find(function (err, charges) {
if (!err) {
return res.json(charges);
} else {
return res.send(err);
}
});
};
exports.publishable_key = function(req, res){
return res.send(config.stripe.publishable_key);
};
exports.submitCharge = function(req, res){
var savedCharge;
var result = stripe.charges.create({
amount: req.body.amount,
currency: "usd",
card: req.body.card,
description: req.body.description
}, function(err, charge) {
if (err) {
console.log('errors');
console.log(err);
} else {
console.log('success');
}
}).then(function(charge){
savedCharge = Charge.create({
name: charge.card.name,
amount: charge.amount,
email: charge.metadata.email,
address: charge.card.address_line1 + charge.card.address_line1,
city: charge.card.address_city,
state: charge.card.address_city,
zip: charge.card.address_zip,
tax_receipt: charge.metadata.tax_receipt,
missionary: charge.metadata.missionary,
});
});
console.log('savedCharge');
console.log(savedCharge);
return res.send(savedCharge);
};
It looks like the Stripe API gives you the ability to use a callback or a promise, but you're using both in your call to charges.create:
var result = stripe.charges.create({
...data...
}, function (err, charge) { // <-- callback
...
}).then(function(charge) { // <-- promise
...
})
You need to choose one or the other. That is, either put the call to Charge.create inside the callback from stripe.charges.create (where your console.log('success') is) or move your error handling to the promise by providing a second argument to the then() statement: a function with an err parameter.

Categories