Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ParseObject still has deleteOperation in field after saveCallback, when using beforeSave in Cloud #1840

Closed
pargu opened this issue May 19, 2016 · 11 comments

Comments

@pargu
Copy link

pargu commented May 19, 2016

Reproduce:

  1. Set remove / removeKey on a field on a ParseObject and save object with block (using beforeSave on Cloud for object - only returning response.success())
  2. Logging object after save has been done in block still contains deleteOperation for the field. On server side all is fine, with the right values set (value removed).
  3. Later on trying to save new value to same field causing "Operation is invalid after previous operation", because you are trying to add to a field that has a deleteOperation still in it and trying to save said object.

Hacks

  1. Add empty string to field instead of remove / removeKey (null checks can't be made later on)
  2. Create a new ParseObject and init old object into it, before saving the second time (Everything goes fine, cache:ing is lost if you need to update something in views in previous screens which are connected to original object. This hack does not support ParseInstallation and ParseUser)
  3. Fetch object again in saveCallback (Not very efficient)
@drew-gross
Copy link
Contributor

Which SDK are you using? This sounds like it might be an SDK issue.

Either way, could you turn on verbose logging for your server with VERBOSE=1 env var, and show the request and response from when you make this call?

@pargu
Copy link
Author

pargu commented May 20, 2016

We are running the latest versions of the Android and iOS SDKs, with the same result for both.
Verbose logging is now also set.

This is a test made with an Android Device, only returning success from before- and aftersave of an object.The test is simple. First "check in" a child-object and set values to it. Later when "Checking out" the child-object you remove some values from it.

Cloud

Parse.Cloud.beforeSave(‘myObjectName’, function(req, resp) {

console.log("BF SAVE KID REQ " +JSON.stringify(req.object));
resp.success();

});

Parse.Cloud.afterSave('myObjectName', function(req, resp) {

console.log("AF SAVE KID REQ" +JSON.stringify(req.object));
resp.success();

});

CHECK IN

BF SAVE KID REQ

{"ACL":{ROLES},"DATE-value":{"__type":"Date","iso":"content"},"String-value":"content","bool":true,"String-value":"content","bool-value":true,"department":"content","pointer-array":[{"__type":"Pointer","className":"_User","objectId":"content"},{"__type":"Pointer","className":"_User","objectId":"content"}],"pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"String-value":"content","String-value":"content","String-value":"content","String-value":"content","pointer":{"__type":"Pointer","className":"dep_con","objectId":"content"},"String-value":"content","kidSick":false,"createdAt":"content","updatedAt":"content","String-value":"content","bool-value":true,”bool-value":true,"bool-value":true,"pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"pickupInfo":"content”,"String-value":"content","pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"String-value":"content","String-value":"content"}

AF SAVE KID REQ

{"ACL":{ROLES},"DATE-value":{"__type":"Date","iso":"content"},"String-value":"content","bool":true,"String-value":"content","bool-value":true,"department":"content","pointer-array":[{"__type":"Pointer","className":"_User","objectId":"content"},{"__type":"Pointer","className":"_User","objectId":"content"}],"pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"String-value":"content","String-value":"content","String-value":"content","String-value":"content","pointer":{"__type":"Pointer","className":"dep_con","objectId":"content"},"String-value":"content","kidSick":false,"createdAt":"content","updatedAt":"content","String-value":"content","bool-value":true,”bool-value":true,"bool-value":true,"pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"pickupInfo":"content”,"String-value":"content","pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"String-value":"content","String-value":"content"}

RESPONSE CHECK IN KID (Client-side, no error):

{"String-value":"content","String-value":"content","bool-value":"true","String-value":"content”,"String-value":"content","String-value":"content","bool-value":"true","ACL":"com.parse.ParseACL@428708b8","String-value":"content","pointer":{“_User”},"pointer":{"_User"},"bool-value":"false","lastName":"content","DATE-value":"content","bool-value":"true","bool-value":"true","bool-value":"true","pointer":{"_User"},"String-value":"content","pointer-array":"[com.parse.ParseUser@42660658, com.parse.ParseUser@42b734c0]","String-value":"content","String-value":"content","pointer":{"_User"},"String-value":"content","String-value":"content"}

Result

This is all fine setting the right values...

CHECK OUT

BF SAVE KID REQ

{"ACL":{"ROLES”},"DATE-value":{"__type":"Date","iso":"content"},"String-value":"content","bool-value":true,"String-value":"content","bool-value":false,"String-value":"content","pointer-array":[{"__type":"Pointer","className":"_User","objectId":"content"},{"__type":"Pointer","className":"_User","objectId":"content"}],"pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"String-value":"content","String-value":"content","String-value":"content","String-value":"content",”pointer”:{"__type":"Pointer","className":"content","objectId":"content"},"String-value":"content","createdAt":"content","updatedAt":"content","String-value":"content","bool-value":true,"bool-value":true,"bool-value":true,"pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"String-value":{"__op":"Delete"},"String-value":{"__op":"Delete"},"pointer":{"__op":"Delete"},"String-value":{"__op":"Delete"},”String-value":"content"}

AF SAVE KID REQ (Still has DeleteOperation)

{"ACL":{"ROLES”},"DATE-value":{"__type":"Date","iso":"content"},"String-value":"content","bool-value":true,"String-value":"content","bool-value":false,"String-value":"content","pointer-array":[{"__type":"Pointer","className":"_User","objectId":"content"},{"__type":"Pointer","className":"_User","objectId":"content"}],"pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"String-value":"content","String-value":"content","String-value":"content","String-value":"content",”pointer”:{"__type":"Pointer","className":"content","objectId":"content"},"String-value":"content","createdAt":"content","updatedAt":"content","String-value":"content","bool-value":true,"bool-value":true,"bool-value":true,"pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"String-value":{"__op":"Delete"},"String-value":{"__op":"Delete"},"pointer":{"__op":"Delete"},"String-value":{"__op":"Delete"},”String-value":"content"}

RESPONSE CHECK OUT KID: (Client-side, no error - Still has DeleteOperation)

{"String-value":"content","String-value":"content","bool-value":"true","String-value":"content","String-value":"content","String-value":"com.parse.ParseDeleteOperation@427557f8","bool-value":"true","ACL":"com.parse.ParseACL@42dffad0","String-value":"content","pointer":{“_User”},"pointer":{"_User"},"bool-value":"false","String-value":"content","DATE-value":"content","bool-value":"true","bool-value":"true","bool-value":"false","pointer":{"anotherObject"},
"whoPickUp":"com.parse.ParseDeleteOperation@427557f8","pointer-array":"[com.parse.ParseUser@42dfe7d0, com.parse.ParseUser@42dfefb0]","String-value":"com.parse.ParseDeleteOperation@427557f8","String-value":"content","pointer":"com.parse.ParseDeleteOperation@427557f8","whoCheckedIn":"content","String-value":"content"}

Result

This does not work. Instead of containing null the updated object still has ParseDeleteOperation in it's fields, when setting remove on a field before saving object. Other fields are updated and Dashboard displays correct values ( (undefined) for the deleted value(s) in field)

Removing Cloud-code before- and aftersave resolves this issue. Issue only occurs when trying to delete value in a field. Update works as it should.

Please get back if you need some further data for debugging the issue

@airdrummingfool
Copy link
Contributor

I'm running into this on both platforms I use (iOS and in JS) when I unset() a key in cloud code on an object that has a beforeSave. I'll write up a test that shows the issue.

@dbarabander
Copy link

dbarabander commented May 29, 2016

We used a workaround of seting an undefined value rather than calling unset @airdrummingfool. Working for us so far with no problems.

@airdrummingfool
Copy link
Contributor

@dbarabander Thanks for the heads up! I'll give that a try.

@dbarabander
Copy link

@airdrummingfool my pleasure

@airdrummingfool
Copy link
Contributor

Submitted PR #1940 with a test as promised. Note that I had to make the beforeSave actually modify the object, contrary to what @pargu stated. This is most likely because of the fix for #1238, which is on master but hasn't landed in a release yet.

@airdrummingfool
Copy link
Contributor

@dbarabander I tried seting to undefined, but my beforeSave function still sees the previous value (instead of undefined), so I can't use that. Can you check and see if this is happening for you as well? I added a separate issue and a test for it at #1942.

@pargu
Copy link
Author

pargu commented Jun 3, 2016

So after updating to the latest server-version everything on iOS works great! There's still an issue on the android side though.

When removing a value of a field on the android side the __op: Delete is still there in afterSave and the updated object in the saveCallback still has it. Not for the iOS side.

This is the server log for running the same save-operation for Android and iOS using .remove('key') and .removeObjectForKey()

Android

BF SAVE KID REQ (has __op:Delete)

{"String-value":"content","DATE-value":"DATE","String-value":"content ","String-value":"content","pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"bool-value":true,"String-value":"content","String-value":"content","bool-value":true,"String.value":"content","String-value":{"__op":"Delete"},"bool-value":true,"DATE":{"__type":"Date","iso":"content"},"String-value":"content","pointer-array":[{"__type":"Pointer","className":"_User","objectId":"content"}],"String-value":"content","pointer":{"__op":"Delete"},"number-value":3,"pointer":{"__type":"Pointer","className":"className","objectId":"content"},"String-value":"content","bool-value":true,"ACL":{"ROLES"},"DATE-value":"DATE","String-value":"content ","bool-value":false,"array-value":[array],"bool-value":false,"String-value":"content"}

AF SAVE KID REQ (Still has __op:Delete)

{"String-value":"content","DATE-value":"DATE","String-value":"content ","String-value":"content","pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"bool-value":true,"String-value":"content","String-value":"content","bool-value":true,"String.value":"content","String-value":{"__op":"Delete"},"bool-value":true,"DATE":{"__type":"Date","iso":"content"},"String-value":"content","pointer-array":[{"__type":"Pointer","className":"_User","objectId":"content"}],"String-value":"content","pointer":{"__op":"Delete"},"number-value":3,"pointer":{"__type":"Pointer","className":"className","objectId":"content"},"String-value":"content","bool-value":true,"ACL":{"ROLES"},"DATE-value":"DATE","String-value":"content ","bool-value":false,"array-value":[array],"bool-value":false,"String-value":"content"}

iOS

BF SAVE KID REQ (has __op:Delete)

{"String-value":"content","DATE-value":"DATE","String-value":"content","String-value":"content","String-value":"content","pointer-value":{"__type":"Pointer","className":"_User","objectId":"content"},"bool-value":true,"String-value":"content","String-value":"content","bool-value":false,"String-value":"content","String-value":{"__op":"Delete"},"bool-value":true,"DATE-value":{"DATE"},"pointer-array":[{"__type":"Pointer","className":"_User","objectId":"content"}],"String-value":"content","pointer":{"__op":"Delete"},"number-value":3,"pointer":{"__type":"Pointer","className":"className","objectId":"content"},"String-value":"content","bool-value":true,"ACL":{"ROLES"} "DATE-value":"DATE","File-value":{"FILE-URL"},"bool-value":false,"bool-value":false,"String-value":"content"}

AF SAVE KID REQ (No __op:Delete)

{"String-value":"content","DATE-value":"DATE","String-value":"content","String-value":"content","String-value":"content","pointer":{"__type":"Pointer","className":"_User","objectId":"content"},"bool-value":true,"String-value":"content","String-value":"content","bool-value":false,"String-value":"content","bool-value":true,"DATE-value":{"DATE"},"pointer-array":[{"__type":"Pointer","className":"_User","objectId":"content"}],"String-value":"content","number-value":3,"pointer":{"__type":"Pointer","className":"className","objectId":"content"},"String-value":"content","bool-value":true,"ACL":{"ROLES"},"DATE-value":"DATE","File-value":{"FILE-URL"},"bool-value":false,"bool-value":false,"String-value":"content"}

@tony-pizza
Copy link

the latest server-version

@pargu just to be clear, what version is that? did you pull directly from github or are you using the one in npm?

@pargu
Copy link
Author

pargu commented Jun 4, 2016

@peeinears We're using 2.2.11 pulled from github and android sdk version (1.13.0)

flovilmart added a commit that referenced this issue Jul 8, 2016
flovilmart added a commit that referenced this issue Jul 12, 2016
flovilmart added a commit that referenced this issue Jul 12, 2016
flovilmart added a commit that referenced this issue Jul 15, 2016
…ons to SDKs (#1946)

* Adding a test demonstrating issue #1840.

* Fixes #1840

* Adds failing test with other use case

- That test fails on parse.com as well

* Bumps parse to 1.9.0

* exclude pg db

* Exclude pg on other test

* Adds clientSDK compatibility check for forward deletion

- Mark js1.9.0 as compatible

* Strips all operations from result

- fix for #1606
rsouzas pushed a commit to back4app/parse-server that referenced this issue Mar 15, 2017
… delete operations to SDKs (parse-community#1946)

* Adding a test demonstrating issue parse-community#1840.

* Fixes parse-community#1840

* Adds failing test with other use case

- That test fails on parse.com as well

* Bumps parse to 1.9.0

* exclude pg db

* Exclude pg on other test

* Adds clientSDK compatibility check for forward deletion

- Mark js1.9.0 as compatible

* Strips all operations from result

- fix for parse-community#1606
rsouzas pushed a commit to back4app/parse-server that referenced this issue Mar 16, 2017
… delete operations to SDKs (parse-community#1946)

* Adding a test demonstrating issue parse-community#1840.

* Fixes parse-community#1840

* Adds failing test with other use case

- That test fails on parse.com as well

* Bumps parse to 1.9.0

* exclude pg db

* Exclude pg on other test

* Adds clientSDK compatibility check for forward deletion

- Mark js1.9.0 as compatible

* Strips all operations from result

- fix for parse-community#1606
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants