parse-server icon indicating copy to clipboard operation
parse-server copied to clipboard

feat: Allow short circuit of beforeFind

Open dblythy opened this issue 1 year ago • 20 comments

Pull Request

Issue

Closes: #8693

Approach

Adds ability to return objects (or an empty array) from a beforeFind trigger.

Tasks

  • [x] Add tests

dblythy avatar Jul 19 '23 03:07 dblythy

I will reformat the title to use the proper commit message syntax.

Thanks for opening this pull request!

Codecov Report

Patch coverage: 83.33% and project coverage change: +0.02% :tada:

Comparison is base (2b3d4e5) 94.33% compared to head (6b2de6c) 94.36%. Report is 1 commits behind head on alpha.

:exclamation: Current head 6b2de6c differs from pull request most recent head d66fa48. Consider uploading reports for the commit d66fa48 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##            alpha    #8694      +/-   ##
==========================================
+ Coverage   94.33%   94.36%   +0.02%     
==========================================
  Files         185      185              
  Lines       14766    14773       +7     
==========================================
+ Hits        13930    13941      +11     
+ Misses        836      832       -4     
Files Changed Coverage Δ
src/rest.js 96.80% <66.66%> (-2.06%) :arrow_down:
src/triggers.js 95.42% <100.00%> (+0.06%) :arrow_up:

... and 3 files with indirect coverage changes

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

codecov[bot] avatar Jul 19 '23 03:07 codecov[bot]

It could cause issues for a strongly typed language such as Swift, if an object is returned that has not been saved (objectId might be required). Not 100% sure but i'm pretty sure triggers are skipped with explain

dblythy avatar Jul 23 '23 13:07 dblythy

Is that really a "strongly typed" question? Or do you mean that (any) Parse client SDK may expect a returned object to have a objectId - regardless of whether it's a strongly typed language or not.

mtrezza avatar Jul 23 '23 13:07 mtrezza

The JS SDK can handle "unsaved" objects, i'm not sure whether other SDKs can just yet.

Regarding the afterFind issue - imo I think it makes sense to skip the afterFind trigger - if there is prevailing logic, it can be called with ES imports

dblythy avatar Jul 23 '23 13:07 dblythy

Then what do you mean by

It could cause issues for a strongly typed language (...), if an object is returned that has not been saved

Doesn't the same argument apply when returning an object from beforeSave? The object can be the same, whether it's returned from beforeFind or afterFind, in both cases the client needs to be able to handle it?

mtrezza avatar Jul 23 '23 15:07 mtrezza

Doesn't the same argument apply when returning an object from beforeSave? The object can be the same, whether it's returned from beforeFind or afterFind, in both cases the client needs to be able to handle it?

Yes you are correct. So I think this should be safe to merge for other SDKs.

dblythy avatar Jul 24 '23 04:07 dblythy

I think so, and I think the docs should explain that, because the developer is interfering in something that Parse Server would do internally, i.e. compose the query results. That's even more difficult than modifying the results in afterFind, because there the developer has as least a "template" of results and can infer what results should look like and that whatever they modify from the original results may cause issues.

I think there are 2 open points:

  • How does a developer know the requirements for composing query results? E.g. does an object in a query result need an objectId, createdAt, updatedAt, etc. SDKs may be able to handle objects without these fields, but maybe not as part of query results, because as part of a query result they usually come from the DB where these fields are always set for stored objects.
  • Should the afterFind trigger be invoked with the custom results returned in beforeFind? It seems to me that there is no reason that it shouldn't. In fact, it may be easier to maintain an existing data pipeline if any existing logic in afterFind applies to these custom results as well. If a developer wants to skip the afterFind logic they can use the context container to set a flag which can be read in the afterFind trigger.

mtrezza avatar Jul 24 '23 11:07 mtrezza

How does a developer know the requirements for composing query results

It's only possible to return Parse Objects, and objectId, createdAt, updatedAt etc are set by default, so it should be fine.

If an object with a different class name is returned, which afterFind trigger should run? The original class name, or the latter?

Parse.Cloud.beforeFind('beforeFind', () => {
  return new Parse.Object('TestObject', { foo: 'bar' });
});
Parse.Cloud.afterFind('beforeFind', (req) => {
 // should this still fire? req.objects[0].className will equal testObject
});
Parse.Cloud.afterFind('TestObject', (req) => {
 // should this?
});

dblythy avatar Jul 24 '23 12:07 dblythy

It's only possible to return Parse Objects, and objectId, createdAt, updatedAt etc are set by default, so it should be fine.

They are set even if a Parse Object is returned when these fields a not set? For example if beforeFind returns:

const MyClass = Parse.Object.extend("MyClass");
const o = new MyClass();
o.a = 1;
return [o];

If an object with a different class name is returned, which afterFind trigger should run? The original class name, or the latter?

So a query on class _Users could return Parse Objects of class _Installation? Looks like trouble ahead... I'm not sure we should allow this. I think we need to at least document some requirements, such as:

  • The return value must be of type Array<ParseObject> when using ParseQuery.find; the array can be empty in case of no results; the return value cannot be undefined.
  • The return value must be of type ParseObject when using ParseQuery.get; the return value can be undefined in case of no result [is that so]?
  • The returned ParseObject's class must be the same class as the invoking ParseQuery's class.

Maybe we should even check in code whether the return value complies with these requirements and throw an error otherwise. This would help developers to ensure they are composing compliant return values.

mtrezza avatar Jul 24 '23 13:07 mtrezza

It’s probably worth mentioning that the same concerns regarding fields, matching className, etc, apply to the current implementation of the afterFind trigger - there aren’t any restrictions what can be returned there (in fact, json that does not conform to a parse object can be returned)

dblythy avatar Jul 24 '23 14:07 dblythy

Yes, I think we discovered a lack of documentation / restriction. We don't know whether Parse Server can handle this change of return values in all scenarios, and for the SDKs we currently don't know how they behave, nor do we have the test set-up to test this out in the CI. Your question already indicates that we are going down a slippery slope.

If we allow (per docs or code) any return type or even return a different class than was queried, then we cannot guarantee that Parse Server or the Parse client SDKs are able to handle this (not just for strongly typed languages).

I think the ParseQuery mechanisms need a framework with restrictions so that the server stays compatible with the SDKs, see list above, and the whole query pipeline (triggers, etc) works. If someone wants to return whatever they like they can use Cloud Functions.

mtrezza avatar Jul 24 '23 17:07 mtrezza

This makes me think about something I'm thinking a lot, It's not better to have a version of parse server only as API? Ignoring that clients' SDKs exist?

For example, I don't use the client SDKs, I have my own handles when I want to have a different return I send a type param (ie: 'search', profileList') and send It to the afterFind as a context to manage to have a different result.

It would give more freedom to work as a backend and make customizations, maybe a key on the config file.

For example, now I want to customize the JSON returning on an aggregation query, but it doesn't work well with Parse Server. And for the use case of this issue would not be a problem, as you don't have to care about client's SDKs.

matheusfrozzi avatar Aug 01 '23 20:08 matheusfrozzi

Ignoring that clients' SDKs exist?

What concept do you have in mind?

For example, now I want to customize the JSON returning on an aggregation query, but it doesn't work well with Parse Server.

With a Cloud Function you are free to return anything.

mtrezza avatar Aug 03 '23 06:08 mtrezza

Ignoring that clients' SDKs exist?

What concept do you have in mind?

Nothing special, just a focused backend version. hehehe

For example, now I want to customize the JSON returning on an aggregation query, but it doesn't work well with Parse Server.

With a Cloud Function you are free to return anything.

Yes for sure, but I don't want to have a lot of endpoints as POST that are supposed to be GET. But as I'm saying, I'm customizing on before/after find and not using the client's and it works. Just have to be organized.

matheusfrozzi avatar Aug 03 '23 12:08 matheusfrozzi

What is a "focused backend version"? Not sure I understand what change you are suggesting.

I don't want to have a lot of endpoints as POST that are supposed to be GET.

I'm sure Cloud Functions could easily support GET requests (if they don't already do this). However this seems to be off topic; if you would like to open a feature request for that please search for existing issues and if necessary open a new issue.

mtrezza avatar Aug 03 '23 19:08 mtrezza

What is a "focused backend version"? Not sure I understand what change you are suggesting.

I don't want to have a lot of endpoints as POST that are supposed to be GET.

I'm sure Cloud Functions could easily support GET requests (if they don't already do this). However this seems to be off topic; if you would like to open a feature request for that please search for existing issues and if necessary open a new issue.

Sorry, I'm suggesting nothing, just this PR made me think about how the client's SDKs can make it harder to do some stuff, but as you said, it's possible to use cloud functions.

matheusfrozzi avatar Aug 03 '23 21:08 matheusfrozzi

Got it, feel free to open a feature request for Cloud Functions for GET requests in a separate issue, if it's not already supported.

mtrezza avatar Aug 04 '23 18:08 mtrezza

@dblythy Do you want to try to get this over the finish line? I believe this is almost done.

mtrezza avatar Sep 07 '23 09:09 mtrezza