UserCannotBeAlteredWithoutSessionError when updating PFUser on Parse from Heroku node.js app - javascript

I'm trying to update a PFUser in my Parse database from a node.js app running on Heroku. I'm calling the Parse cloud function from an iOS app.
Here's the part of the code I use to update the user on parse as well as creating the user on Stripe (the Stripe part works fine):
Parse.Cloud.define("createCustomerWithCreditCardToken", function(request, response) {
var userId = request.user.id;
var sourceId = request.params.creditCardToken;
var customerId;
var userSessionToken = request.user.getSessionToken();
console.log('userId: ' + userId + ' source: ' + sourceId + ' userSessionToken: ' + userSessionToken);
stripe.customers.create({
source: sourceId,
description: userId
}, function(error, customer) {
if (error !== null) {
response.error('error creating customer: ' + error);
}else {
var userQuery = new Parse.Query('User');
userQuery.equalTo('objectId', userId);
userQuery.first({sessionToken: userSessionToken}).then(function(user) {
console.log('user from parse query: ' + user.get("username"));
user.set("stripeCustomerId", customer.id);
user.save(null, {
success: function(parseCustomer) {
console.log('customer saved to parse: ' + parseCustomer);
},
error: function(error, parseCustomer) {
console.log('customer save failed: ' + JSON.stringify(error, null, 2) + ' with error: ' + JSON.stringify(parseCustomer,null, 2));
}
});
});
customerId = customer.id;
console.log('customerId: '+ customerId);
// response.success(customer);
response.success('Customer: ' + JSON.stringify(customer, null, 2) + 'error: ' + error);
}
});
});
I get the following error log output when I run this:
error log output
error: { "code": 206, "message": "Parse::UserCannotBeAlteredWithoutSessionError" }
In this post the current user concept in a node.js app context is discussed by a Parse engineer.
Also in Cloud Code, the concept of a method that returns the current
user makes sense, as it does in JavaScript on a web page, because
there’s only one active request and only one user. However in a
context like node.js, there can’t be a global current user, which
requires explicit passing of the session token.
Essentially he advises to do this:
Parse.Cloud.define('findBacon', function(req, res) {
var token = req.user.getSessionToken();
var query = new Parse.Query('Bacon');
// Pass the session token to the query
query.find({ sessionToken: token }).then( ... );
});
I have also tried using {useMasterKey:true} instead of {sessionToken:userSessionToken} which produces the same error.
I might just be missing some obvious detail, but I just can't seem to find it. Any ideas on how to solve this are greatly appreciated.

Turns out there's a third way of handling credentials:
Parse.Cloud.useMasterKey();
I placed this line in the beginning of the entire method, that did it for me. I'm not sure of the implications of giving the whole function these credentials though.
I'm not sure when you would use the other options either.
If someone comes across this and would like to elaborate, I'll be happy to give the right answer to a good explanation of when to grant which credentials.

Related

Javascript and Django Rest Framework - cannot read property

I'm trying to retrieve data from an API endpoint that i created with Django Rest Framework. At first, my code worked but now i keep getting this error:
Uncaught TypeError: Cannot read property 'ticker' of undefined
It's weird, because the console.logstatement in the following code returns nothing, although i'm sure that there is data in the endpoint that i'm calling.
This is what the data i'm trying to retrieve looks like:
[
{
ticker: "TEST",
Price: 7876
}
]
And this is the Ajax function that i built:
function doPoll(){
$.getJSON('http://127.0.0.1:8000/tst/', function(data) {
console.log(data[0]);
$('#data').text(data[0].ticker + ' ' + data[0].Price);
setTimeout(doPoll, 100);
});
}
doPoll();
And this is where the data should appear:
<h3 id="data"></h3>
The function should be supposed to place my data on my html page and refresh that data every second.
http://127.0.0.1:8000/tst/ is the DRF API endpoint that i'm trying to call, from where i'm retrieving the JSON data.
you need to access data.results... this is standard DRF response format
function doPoll(){
$.getJSON('http://127.0.0.1:8000/tst/', function(data) {
var results = data.results;
console.log(results[0]);
$('#data').text(results[0].ticker + ' ' + results[0].price);
setTimeout(doPoll, 100);
});
}
doPoll();
You have a typo here data[0].Price should be data[0].price
$.getJSON('http://127.0.0.1:8000/tst/', function(data) {
if(data && data.length > 0){
console.log(data[0]);
$('#data').text(data[0].ticker + ' ' + data[0].Price);
setTimeout(doPoll, 100);
}else{
//console.log('No Data')
console.log("DATA is: ", data)
}
});
Validate that data[0] exists before accessing properties of it
The best way to debug this is to check the response in your browser dev tools
Network tab

Firebase function - http get parameters with acentuation

I have a firebase function doing a http GET. There are 3 parameters and all works ok but if one of the parameters contains acentuation the Firebase console don't show any error but the the GET is not executed. In this case, the problem is with Parameter03.
var url = 'http://myapi.azurewebsites.net/api/values?Parameter01=' + nameParam + '&Parameter02=' + emailParam + '&Parameter03=' + serviceParam ;
http.get(url, (resp) => {
res.setEncoding('utf8');
}).on("error", (err) => {
console.log("Error : " + err.message);
});
Any help please ?
Whenever you build a URL, you should properly escape all the query string components so that they contain only valid characters. That's what encodeURIComponent() is for. So do encode all your query string values like this instead:
var url = 'http://myapi.azurewebsites.net/api/values' +
'?Parameter01=' + encodeURIComponent(nameParam) +
'&Parameter02=' + encodeURIComponent(emailParam) +
'&Parameter03=' + encodeURIComponent(serviceParam);
There are other cleaner ways to build a URL with query string components, but this should work fine.

google api javascript get logged in user's email

There are many resources and stack overflow questions that are similar but not exactly the same as what I will ask. I will rehash some of the solutions here and explain them.
I have a user that is already logged into Google. By logged in I mean manually logged in and the cookie is present. Not logged in by my application.
I just need to get the email address.
There are 3 ways to do this that I have seen but neither works for me.
Way #1:
auth2 = gapi.auth2.getAuthInstance();
if (auth2.isSignedIn.get()) {
var profile = auth2.currentUser.get().getBasicProfile();
console.log('ID: ' + profile.getId());
console.log('Full Name: ' + profile.getName());
console.log('Given Name: ' + profile.getGivenName());
console.log('Family Name: ' + profile.getFamilyName());
console.log('Image URL: ' + profile.getImageUrl());
console.log('Email: ' + profile.getEmail());
}
Way #2:
gapi.client.setApiKey(API_KEY);
gapi.client.load("plus", "v1", function() {
gapi.client.plus.people.get({ userId: "me" }).execute(function(resp) {
// Shows profile information
console.log(resp);
});
});
Way #3:
gapi.client.load('oauth2', 'v2', function () {
gapi.client.oauth2.userinfo.get().execute(function (resp) {
// Shows user email
console.log(resp.email);
})
});
For Way #2 and Way #3 stack overflow says that you have to use a token and not an api key. But the user is already logged in and I don't have and cannot obtain the token.
How to get the EMAIL of an ALREADY logged in user?
Thanks
Though an old question.. this may help .. just incase..
var auth2 = gapi.auth2.getAuthInstance();
var profile = auth2.currentUser.get().getBasicProfile();
console.log(profile.getName());
console.log(profile.getEmail());
The instance can be initiated by either gapi.auth2.getAuthInstance() or gapi.auth2.init(). Based on what is used to instantiate you can use either to get profile details.

Parse Javascript - Adding user error

I created a factory in angularJS to store data to Parse User object. Every time I click to process the details and open an account for the user, it shows the following error in console:
Failed to load resource: https://api.parse.com/1/users the server
responded with a status of 400 (Bad Request)
I checked if the service parameter is valid with attributes content, and yes it has content (not empty). This is the service body:
.factory('CreateUserService', function() {
return {
createAccount: function(user) {
console.log("Service User: " + user.name);
var parseUser = new Parse.User();
parseUser.set("username", user.username);
parseUser.set("email", user.username);
parseUser.set("name", user.username);
parseUser.set("password", user.username);
parseUser.set("mobile", user.username);
/*Attempt to create user in DB*/
parseUser.signUp(null, {
success: function(parseUser) {
// Hooray! Let them use the app now.
//alert("success!");
return 1;
},
error: function(parseUser, error) {
// Show the error message somewhere and let the user try again.
//alert("Error: " + error.code + " " + error.message);
return error;
}
});
}
}
});
I added the Parse CDN in the index.html of my application with:
<script src="http://www.parsecdn.com/js/parse-1.6.7.min.js"></script>
Update: In Parse API Guide, this is the body of the signup:
user.signUp(null, {
success: function(user) {
// Hooray! Let them use the app now.
},
error: function(user, error) {
// Show the error message somewhere and let the user try again.
alert("Error: " + error.code + " " + error.message);
}
});
The user object in Parse (I review through the browser) is not showing any changes to the rows number hence user not added. Any idea what is going wrong?
Thanks
Update: When I deleted the existing empty user object in Parse and ran the code, it worked and created a user Object with all columns specified in the code. Then if I try to add another user, I get a POST error. There must be a way around this since signup method by parse surely considered adding multiple users to the User object.

Parse Cloud Code: Can you pass objects in a push notification?

I'm creating a push notification similar to this sample code provided by Parse:
Parse.Cloud.afterSave('Activity', function(request) {
if (request.object.get("type") === ("comment") {
var message = request.user.get('displayName') + ': ';
message += request.object.get('content').trim();
var query = new Parse.Query(Parse.Installation);
query.equalTo('user', request.object.get("toUser"));
Parse.Push.send({
where:query,
data: {
alert: message,
badge: 'Increment'
}
});
}
});
My question is: in the data area of the Parse.Push.send, can I send an entire message object, where Message is a custom class I created? If so, what would that look like?
If you already have the class created and saved an object why not just send the object ID and query it asynchronously once the user goes to retrieve the push notification?
The message object does not need to waste up space and be sent via push just a pointer is required.
I'm in the process of implementing something similar and this is the route I plan to use.
You can serialize the object in JSON/XML format and then deserialize it when you receive the push notification.
You can't send objects directly. You'll get an exception (I had this issue a few days ago but didn't write the name of the exception down). The best answer is so far is by BrentonGray88.
I'm assuming you're not using Android because you included the badge: "Increment" value, but this is how I would do it:
Android code to send the notification:
Make sure the Comment object has a pointer (_User) column to the User who sent the comment. When you create the comment, include user.put("commentAuthor", ParseUser.getCurrentUser()); in your Android code so that you can always access the user who created the comment.
Now you need to query the Comment to send its objectId through to the push notification.
ParseQuery<ParseObject> query = new ParseQuery<>("Comment");
query.whereEqualTo("objectId", I AM NOT SURE WHAT CONDITION YOU WANT HERE);
query.findInBackground((comment, e) -> {
if (e == null) for (ParseObject commentObject: comment) {
String recipientObjectId = commentObject.getParseObject("commentAuthor").getObjectId();
final Map<String, Object> params = new HashMap<>();
// This is to send the notification to the author of the Comment
params.put("recipientObjectId", recipientObjectId);
// This is so we can use values from the Comment in the notification
params.put("commentObjectId", commentObject.getObjectId());
// This is a required lined
params.put("useMasterKey", true);
ParseCloud.callFunctionInBackground("pushMessage", params, new FunctionCallback<String>() {
public void done(String result, ParseException e) {
if (e == null) {
Log.d(getClass().toString(), "ANNOUNCEMENT SUCCESS");
} else {
System.out.println(e);
Log.d(getClass().toString(), "ANNOUNCEMENT FAILURE");
}
}
});
}
});
Now for the query in your Cloude Code:
Parse.Cloud.define("pushMessage", function (request, response) {
// Again, we are sending the notification to the author of the Comment
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.equalTo('user', request.params.get("recipientObjectId"));
// We retrieve information from the Comment created by that author
var commentQuery = new Parse.Query(Parse.Comment);
commentQuery.equalTo('objectId', request.params.commentObjectId);
commentQuery.get("commentObjectId", {
success: function(userObject) {
var displayName = userObject.get("displayName");
var content = userObject.get("content");
var message = displayName + ': ';
message += content.trim();
Parse.Push.send({
where: pushQuery,
data: {
alert: message
},
}, {
useMasterKey: true,
success: function () {
response.success("Success!");
},
error: function (error) {
response.error("Error! " + error.message);
}
});
console.log();
},
error: function(error) {
console.log("An error occured :(");
}
});
});
Sorry I'm not the best at JavaScript but that's kind of how I'd do it. Best of luck! :)

Categories

Resources