I'm trying to add a relation on the table users with the bars on the table bar. The bars already exist on my database but I'm always getting the error "unable to encode an association with an unsaved ParseObject".
ParseUser user = ParseUser.getCurrentUser();
ParseObject barParseObject = ParseObject.createWithoutData(TABLE_BAR, bar.getBarId());
ParseRelation<ParseObject> relation = user.getRelation(ApiUserMapper.FIELD_LIKES);
if (bar.isFavourite()) {
relation.add(barParseObject);
} else {
relation.remove(barParseObject);
}
user.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
resultCallback.processResults(bar);
} else {
if (BuildConfig.DEBUG) {
Log.e(TAG, "Error: updateFav " + e.getMessage());
}
resultCallback.processError(e);
}
}
});
bar.getBarId() contains a valid ObjectId already stored in the database, but I also tried querying for that bar before with the same result.
ParseQuery<ParseObject> query = ParseQuery.getQuery(TABLE_BAR);
query.getInBackground(bar.getBarId(), new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (e == null) {
ParseRelation<ParseObject> relation = user.getRelation(ApiUserMapper.FIELD_LIKES);
if (bar.isFavourite()) {
relation.add(object);
} else {
relation.remove(object);
}
user.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
resultCallback.processResults(bar);
} else {
if (BuildConfig.DEBUG) {
Log.e(TAG, "Error: updateFav " + e.getMessage());
}
resultCallback.processError(e);
}
}
});
}
}
});
If I try to add the relation the other way around, it works fine. But I want the relation to be added on the table User.
ParseObject barParseObject = ParseObject.createWithoutData(TABLE_BAR, bar.getBarId());
ParseRelation<ParseObject> relation = barParseObject.getRelation(ApiUserMapper.FIELD_LIKES);
relation.add(user);
barParseObject.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
resultCallback.processResults(bar);
} else {
if (BuildConfig.DEBUG) {
Log.e(TAG, "Error: updateFav " + e.getMessage());
} else {
Crashlytics.log(LOG_PRIORITY, TAG, "Error: updateFav " + e.getMessage());
}
resultCallback.processError(e);
}
}
});
Any hints? Something related to permissions? I'm assuming that the current user is able to write his own data but I don't have any specific setting anywhere in my code.
Thanks!
Are you sure the user is logged in? It looks like the user.getCurrentUser() may be returning null, which would explain the sticky error.
I don't know what was going on but I deleted my user on the Parse console. I created a new one and now everything it's working fine!
I can't comment or upvote yet but I wanted to let it be known that deleting and readding the user also fixed my problem, in case someone else has the same issue.
Related
Welcome all!
I am currently working to pass a token generated by Stripes API from an Android app to a ParseServer. Below is my code, please be advised that I commented out previous failed attempts to let you know what I have tried and to also spark your imagination. Please note that with trial and error the issue presents to be with saving the data to the server. I have double checked that the class User has write permissions and it has an Object attribute titled token.
// Test the data.
if (userCard.validateCard()) {
Stripe stripe = new Stripe(CardActivity.this, "correct data is here I removed it, for StackOverflow");
stripe.createToken(
userCard,
new TokenCallback() {
public void onSuccess(final Token token) {
// Send token to your server
// Query the users, and get the username.
ParseQuery<ParseObject> query = ParseQuery.getQuery("User");
ParseUser user = ParseUser.getCurrentUser();
String objectId = user.getObjectId();
// Query the current user.
//query.whereEqualTo("objectId", username);
ParseObject object;
try {
object = query.get(objectId);
object.put("token", token);
object.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Toast.makeText(CardActivity.this, "Success", Toast.LENGTH_SHORT).show();
}
}
});
} catch (ParseException e) {
Toast.makeText(CardActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
// Attempt to update... Currently not working.
/*query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> objects, ParseException e) {
if (e == null && objects != null) {
for (ParseObject object : objects) {
object.put("token", token);
object.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Toast.makeText(CardActivity.this, "Success", Toast.LENGTH_SHORT).show();
}
}
});
}
} else {
Toast.makeText(CardActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
});*/
}
public void onError(Exception error) {
// Show error message
Toast.makeText(CardActivity.this,
error.getMessage(),
Toast.LENGTH_LONG
).show();
}
}
);
} else {
Toast.makeText(CardActivity.this, "Something went wrong", Toast.LENGTH_SHORT).show();
}
A few things here:
1) Parse allows you to add/overwrite information to objects from their "shell". This is a ParseObject instance, set to a specified class, assigned an objectId, and then whatever values you want to add/change, and then saved. Any field you did not assign a value to will be ignored, so say you only set field3, field2 and field1 will not be overwritten to nothing.
2) ParseUser user = ParseUser.getCurrentUser() already returns a user object. You don't even need to create a shell from the id, this is a fully functioning parse object. You can then set the value you want and save it!
3) Queries and fetches (and cloud function calls) are asynchronous. This means that the code executes over time on a background thread, and your main thread will go on. So, things that require the results of these methods need to be called within the completion handler. You're doing object = query.get(objectId), but query.get() takes a bit to run so you're probably running through the rest of the code block before object has a proper value.
4) To my knowledge (not an Android developer, but I've used the JS and iOS SDKs) the Parse SDKs have a specific query for the User class that is a bit easier and safer to use than creating a ParseQuery set to the "User" class. Should be something like ParseUser.query()
So, not being an Android developer, I think what you want is more like this:
// Test the data.
if (userCard.validateCard()) {
Stripe stripe = new Stripe(CardActivity.this, "correct data is here I removed it, for StackOverflow");
stripe.createToken(
userCard,
new TokenCallback() {
public void onSuccess(final Token token) {
// Send token to your server
// Query the users, and get the username.
ParseQuery<ParseObject> query = ParseQuery.getQuery("User");
ParseUser user = ParseUser.getCurrentUser();
try {
user.put("token", token);
user.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Toast.makeText(CardActivity.this, "Success", Toast.LENGTH_SHORT).show();
}
}
});
} catch (ParseException e) {
Toast.makeText(CardActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
public void onError(Exception error) {
// Show error message
Toast.makeText(CardActivity.this,
error.getMessage(),
Toast.LENGTH_LONG
).show();
}
}
);
} else {
Toast.makeText(CardActivity.this, "Something went wrong", Toast.LENGTH_SHORT).show();
}
The Parse android SDK does not allow updating of a column in the user table
while I am using getCurrentUser() method to mark it as authenticated. When I call saveInBackground() on it I get the following error in the log file:
Uncaught internal server error. { [MongoError: exception: Mod on _id not allowed]
name: 'MongoError'
Below is the code I am using for saving:
byte[] data = "Working at Parse is great!".getBytes();
final ParseFile file = new ParseFile("abcdef.txt", data);
ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser != null) {
// do stuff with the user
currentUser.put("column_name", file);
currentUser.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Log.i("KQ", "update successfully");
} else {
Log.i("KQ", "update error e = " + e);
}
}
});
} else {
// show the signup or login screen
Log.i("KQ", "else");
}
You have to save the ParseFile before attaching it to the User object.
Your code should be like this
byte[] data = "Working at Parse is great!".getBytes();
final ParseFile file = new ParseFile("abcdef.txt", data);
file.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if(e==null)
{
ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser != null) {
// do stuff with the user
currentUser.put("column_name", file);
currentUser.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Log.i("KQ", "update successfully");
} else {
Log.i("KQ", "update error e = " + e);
}
}
});
} else {
// show the signup or login screen
Log.i("KQ", "else");
}
}
}
});
Till I get a good answer.I am currently using my cloud code beforeSave trigger in parse to remove the id field to get rid of the exception and unblock my work.
any good answer will still be appreciated.
code I am using right now is as follows in the cloud code.
Parse.Cloud.beforeSave(Parse.User, function(request, response) {
// For Android SDK remove id field while updating a user becasue it was creating MongoDB Exception in android.
if (request.object.get("updatedFrom") == 'android') {
request.object.unset("updatedFrom");
request.object.unset("id");
}
response.success();
});
thanks.
I'm beginner on Android and I'm looking for a way on how can I check if a username exists already in Parse data class(User) in Android.
btnreg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ParseUser user = new ParseUser();
user.setUsername(etmail.getText().toString());
user.setPassword(etpwd.getEditableText().toString());
user.put("nom_prenom", etname.getText().toString());
user.put("tel", ettel.getText().toString());
user.put("adresse", adresse.getText().toString());
user.put("sexe", spsexs.getSelectedItem().toString());
user.put("annee_naissance",spnaissance.getSelectedItem().toString());
user.put("ville", spville.getSelectedItem().toString());
user.signUpInBackground(new SignUpCallback() {
public void done(ParseException e) {
if (e == null) {
// Show a simple Toast message upon successful registration
} else {
alerte("Oupss!!", "Error..." + e.getMessage(), 0);
}
}
public void done(com.parse.ParseException e) {
alerte("Succes", "Inscription terminée..", 1);
}
});
I want to check if a username exist in parse : if it exist i want to show an error or a message ("username already taken").
Why do you want do to this ? parse will do that itself for you.
You can track this using ParseException object that you get in SignUpCallback's done method.
Try to get message of that ParseException.
It might be helpful for you to check the ParseUI module, they have all those scenarios covered. Here is a snippet from ParseSignupFragment.java that does the validation:
user.signUpInBackground(new SignUpCallback() {
#Override
public void done(ParseException e) {
if (isActivityDestroyed()) {
return;
}
if (e == null) {
loadingFinish();
signupSuccess();
} else {
loadingFinish();
if (e != null) {
debugLog(getString(com.parse.ui.R.string.com_parse_ui_login_warning_parse_signup_failed) +
e.toString());
switch (e.getCode()) {
case ParseException.INVALID_EMAIL_ADDRESS:
showToast(com.parse.ui.R.string.com_parse_ui_invalid_email_toast);
break;
case ParseException.USERNAME_TAKEN:
showToast(com.parse.ui.R.string.com_parse_ui_username_taken_toast);
break;
case ParseException.EMAIL_TAKEN:
showToast(com.parse.ui.R.string.com_parse_ui_email_taken_toast);
break;
default:
showToast(com.parse.ui.R.string.com_parse_ui_signup_failed_unknown_toast);
}
}
}
}
});
I've searched everywhere but haven't found anything about how to check if a username/email address in the parse database is taken. I've found some answers on the internet on parse.com forums but they weren't clear.
Thanks if you could help.
If this has an answer somewhere, then please comment instead of marking so I can delete it.
I think this will do what you need if I understand your question correctly:
final ParseQuery<ParseUser> emailQuery = ParseUser.getQuery();
emailQuery.whereEqualTo("email", emailAddress);
final ParseQuery<ParseUser> usernameQuery = ParseUser.getQuery();
usernameQuery.whereEqualTo("email", username);
List<ParseQuery> queries = new ArrayList<>();
queries.add(emailQuery);
queries.add(usernameQuery);
final ParseQuery<ParseUser> query = ParseQuery.or(queries);
query.findInBackground(new FindCallback<ParseUser>() {
public void done(List<ParseUser> results, ParseException e) {
// results has the list of users that match either the email address or username
}
});
https://www.parse.com/docs/android/guide#queries-compound-queries
Or you could do it this way:
user.signUpInBackground(new SignUpCallback() {
public void done(ParseException e) {
if (e == null) {
// yippee!!
} else {
switch (e.getCode()) {
case ParseException.USERNAME_TAKEN: {
// report error
break;
}
case ParseException.EMAIL_TAKEN: {
// report error
break;
}
default: {
// Something else went wrong
}
}
}
}
});
Try this to check your data in parse. I hope this will helpful for you.
// this is create query of your parse table
ParseQuery<ParseObject> query = new ParseQuery<>(your prars tableName);
query.whereEqualTo(YourParseColumeNameEmail, yourEmail);
query.whereEqualTo(YourParseColumeNamePassword, yourPassword);
// this is for doing in background
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> scoreList, ParseException e) {
if (e == null) {
if (scoreList.size() == 0) {
// if there is no data like your email and password then it,s come here
} else {
// if there is data like your email and password then it,s come here
}
} else {
Log.d("score", "Error: " + e.getMessage());
}
}
});
The issue is straightforward:
Create a user and add it to a role
Create an object and give it role ACL
The user will be able to find and pin the object, however, the object cannot be retrieved from the local datastore. If I switch to user or public ACL, the object is retrieved.
Example:
public void findAndPinInBackground(ParseQuery<ParseObject> query, final MyCallback callback) {
query.findInBackground(new FindCallback<ParseObject>() {
public void done(final List<ParseObject> eventList, ParseException e) {
if (e == null) {
ParseObject.pinAllInBackground(eventList, new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Log.d(TAG, "Pinned " + eventList.size() + " objects");
callback.onTaskCompleted();
} else {
callback.onTaskFailed(e);
}
}
});
} else {
callback.onTaskFailed(e);
}
}
});
}
Log: "Pinned 1 objects"
ParseQueryAdapter<ParseObject> eventsAdapter = new ParseQueryAdapter<ParseObject>(getActivity(), new ParseQueryAdapter.QueryFactory<ParseObject>() {
public ParseQuery<ParseObject> create() {
ParseQuery query = new ParseQuery("Event");
query.orderByAscending("name");
query.fromLocalDatastore();
return query;
}
}, android.R.layout.simple_list_item_1);
eventsAdapter.addOnQueryLoadListener(new ParseQueryAdapter.OnQueryLoadListener<ParseObject>() {
#Override
public void onLoading() { }
#Override
public void onLoaded(List<ParseObject> parseObjects, Exception e) {
Log.d(TAG, "Retrieved " + parseObjects.size() + " objects");
}
});
Log: "Retrieved 0 objects"
Facebook has confirmed this is a known bug.
Is there a workaround?
The iOS documentation now mentions this:
To access this data protected by Role based ACLs, you will need to
ignore ACLs when executing a Local Datastore query:
PFQuery *query = [[[PFQuery queryWithClassName:#"Note"]
fromLocalDatastore]
ignoreACLs];
https://parse.com/docs/ios/guide#local-datastore-security
It seems the same applies to Android, which has the ignoreACLs in the API:
public ParseQuery<T> ignoreACLs()
Ignore ACLs when querying from the Local Datastore.
This is particularly useful when querying for objects with Role based
ACLs set on them.
https://parse.com/docs/android/api/com/parse/ParseQuery.html#ignoreACLs%28%29
I also experience the same issue so what i did is the following:
Before you pin the object to the local data store do the following:
ParseACL acl = user.getACL();
// Read access only for the logged in user (not public!)
acl.setReadAccess(ParseUser.getCurrentUser(),true);
user.setACL(acl);
user.pinInBackground();
Hope it helps.