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

Properly handle return values in beforeSave #5228

Merged
merged 7 commits into from
Mar 14, 2019
Merged

Properly handle return values in beforeSave #5228

merged 7 commits into from
Mar 14, 2019

Conversation

blastoy
Copy link
Contributor

@blastoy blastoy commented Dec 6, 2018

Closes: #5227

a possible bug found where beforeSave does not apply changes to request
object if the beforeSave hook ends with 'true' and promises returned

a possible bug found where beforeSave does not apply changes to request
object if the beforeSave hook ends with 'true' returned
@flovilmart
Copy link
Contributor

Awesome! Thanks! Tell me, would you be tempted to attempt a fix?

@blastoy
Copy link
Contributor Author

blastoy commented Dec 6, 2018

I leveled up and I just got a quest!

Sure, I’ll give it a try 😊

@flovilmart
Copy link
Contributor

Thanks @blastoy!

@blastoy
Copy link
Contributor Author

blastoy commented Dec 7, 2018

OK, I've narrowed down the problem to this particular area:

if (
response &&
!request.object.equals(response) &&
request.triggerName === Types.beforeSave
) {
return resolve(response);
}
response = {};
if (request.triggerName === Types.beforeSave) {
response['object'] = request.object._getSaveJSON();
}
return resolve(response);

In the case where I add return true at the end of the beforeSave hook, the first if statement will resolve the response, which ends up being just a raw Boolean true.

In the case where I don't add return true at the end of the beforeSave hook, the first if statement is skipped and instead the second if statement executes, later on resolving an object that looks something like { object: {...} }.

What is the intent of the first if statement?

You can see some of the effects of what happens when I write return true at the end of the beforeSave hook below:

That object passed to logTriggerSuccessBeforeHook is just a raw Boolean true

var { success, error } = getResponseObject(
request,
object => {
logTriggerSuccessBeforeHook(
triggerType,
parseObject.className,
parseObject.toJSON(),
object,
auth
);
if (
triggerType === Types.beforeSave ||
triggerType === Types.afterSave
) {
Object.assign(context, request.context);
}
resolve(object);
},

That response in the then is just a raw Boolean true, response.object being undefined. No fieldsChangedByTrigger will be found so its like no changes were done to the object

.then(response => {
if (response && response.object) {
this.storage.fieldsChangedByTrigger = _.reduce(
response.object,
(result, value, key) => {
if (!_.isEqual(this.data[key], value)) {
result.push(key);
}
return result;
},
[]
);
this.data = response.object;
// We should delete the objectId for an update write
if (this.query && this.query.objectId) {
delete this.data.objectId;
}
}
});

@codecov
Copy link

codecov bot commented Dec 8, 2018

Codecov Report

Merging #5228 into master will decrease coverage by 0.03%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master   #5228      +/-   ##
=========================================
- Coverage   93.93%   93.9%   -0.04%     
=========================================
  Files         123     123              
  Lines        8975    9012      +37     
=========================================
+ Hits         8431    8463      +32     
- Misses        544     549       +5
Impacted Files Coverage Δ
src/triggers.js 94.49% <100%> (+0.18%) ⬆️
src/Adapters/Storage/Mongo/MongoStorageAdapter.js 91.48% <0%> (-0.73%) ⬇️
src/ParseServer.js 96.26% <0%> (-0.48%) ⬇️
...dapters/Storage/Postgres/PostgresStorageAdapter.js 96.99% <0%> (-0.16%) ⬇️
src/RestQuery.js 96% <0%> (-0.11%) ⬇️
src/Options/Definitions.js 100% <0%> (ø) ⬆️
src/rest.js 98.85% <0%> (ø) ⬆️
...rc/Adapters/Storage/Mongo/MongoSchemaCollection.js 95.29% <0%> (ø) ⬆️
src/Controllers/SchemaController.js 96.39% <0%> (+0.07%) ⬆️
src/Controllers/DatabaseController.js 95.08% <0%> (+0.15%) ⬆️
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2d7b992...16e947b. Read the comment docs.

also changed test cases to be more descriptive + added extra test case that returns promise in the beforeSave
@blastoy
Copy link
Contributor Author

blastoy commented Dec 14, 2018

My solution to the bug was to add some code right after the beforeSave trigger finishes that ignores whatever the hook returned. Figured if beforeSave works when the hook returns nothing, then making sure returns are ignored would work. Let me know what you guys think!

@dplewis
Copy link
Member

dplewis commented Dec 14, 2018

You have some failing tests. Please check Travis.

@dplewis
Copy link
Member

dplewis commented Dec 27, 2018

@blastoy Ping?

@blastoy
Copy link
Contributor Author

blastoy commented Jan 8, 2019

Hey @dplewis, sorry was away for the holidays. The failing test cases are failing for me regardless of whether I run the tests on a completely clean version of the master branch. I’m not too sure what is causing this. That’s where I left off before the break.

Sent with GitHawk

@dplewis
Copy link
Member

dplewis commented Jan 18, 2019

@blastoy Sorry for the late reply. Without looking into it, since you did change the functionality of the beforeSave, some of the beforeSave tests are failing. Update those tests to match your changes.

@stale
Copy link

stale bot commented Mar 4, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Mar 4, 2019
@stale stale bot closed this Mar 11, 2019
@dplewis dplewis removed the wontfix label Mar 12, 2019
@dplewis dplewis reopened this Mar 12, 2019
@dplewis
Copy link
Member

dplewis commented Mar 13, 2019

@acinader I fixed the PR to address the original issue. It was discovered that returning in beforeSave was unnecessary and could lead to the hook not working. I can update the docs to address this.

@acinader
Copy link
Contributor

this doesn't make sense to me.

  1. you shouldn't have to use async/await
  2. you should be able to return a promise and if it fails the save is aborted.

@dplewis
Copy link
Member

dplewis commented Mar 13, 2019

you should be able to return a promise and if it fails the save is aborted.

This works fine, the issue is if the promise succeeds.

This works

Parse.Cloud.beforeSave('Insurance', function(req) {
  req.object.set('field', 'foo');
  return req.object;
});

This works

Parse.Cloud.beforeSave('Insurance', function(req) {
  req.object.set('field', 'foo');
  return new Promise(resolve => {
    resolve(req.object);
  });
});

This fails

Parse.Cloud.beforeSave('Insurance', function(req) {
  req.object.set('field', 'foo');
  return new Promise(resolve => {
    resolve('almost anything but req.object');
  });
});

Anything returned in the promise will be passed along as a response which prevents req.object from being passed.

@acinader
Copy link
Contributor

the failing case is legal though. and in the case where you're calling a function that may reject or may resolve, then what?

I suspect that @blastoy had it right with "That response in the then is just a raw Boolean true, response.object being undefined. No fieldsChangedByTrigger will be found so its [sic] like no changes were done to the object"

If I've got it right, there are two options to solve:

  1. document that the return object must be a promise that resolves to the object to save (which would be a breaking change from 2.x)

  2. fix the logic to not fail if trigger resolves with any value which I think is consistent with 2.x and would be desireable.

@dplewis dplewis changed the title added failing test case to CloudCode.spec.js Properly handle return values in beforeSave Mar 14, 2019
@dplewis dplewis requested a review from acinader March 14, 2019 17:19
@dplewis
Copy link
Member

dplewis commented Mar 14, 2019

fix the logic to not fail if trigger resolves with any value which I think is consistent with 2.x and would be desireable.

@acinader @blastoy I went this route let me know if more tests are needed

Copy link
Contributor

@acinader acinader left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

I added a test to confirm that if you reject it won't change values. I couldn't find a test that did that and I looked!

If it all looks good to you, merge away.

@dplewis
Copy link
Member

dplewis commented Mar 14, 2019

Good catch on that test!

@blastoy Thanks for the PR!

@dplewis dplewis merged commit bf033be into parse-community:master Mar 14, 2019
UnderratedDev pushed a commit to UnderratedDev/parse-server that referenced this pull request Mar 21, 2020
* added failing test case to CloudCode.spec.js

a possible bug found where beforeSave does not apply changes to request
object if the beforeSave hook ends with 'true' returned

* moddified triggers to return null when beforeSave
also changed test cases to be more descriptive + added extra test case that returns promise in the beforeSave

* address original issue

* Revert "address original issue"

This reverts commit e01c57d.

* fix promises and tests

* Add a test to verify that a failed beforeChange hook will
prevent updating the object.
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

Successfully merging this pull request may close these issues.

4 participants