feat: Allow short circuit of beforeFind
Pull Request
- Report security issues confidentially.
- Any contribution is under this license.
- Link this pull request to an issue.
Issue
Closes: https://github.com/parse-community/parse-server/issues/8693
Approach
This PR continues and supersedes #8694 by @dblythy. Adds ability to return objects (or an empty array) from a beforeFind trigger.
Tasks
- [x] Add tests
Summary by CodeRabbit
-
New Features
- Improved support for returning objects or arrays of objects directly from cloud triggers, allowing for more flexible query responses.
-
Bug Fixes
- Ensured that objects returned from triggers are handled correctly without unnecessary conversions.
-
Tests
- Added comprehensive tests to verify the behavior of triggers returning objects or arrays, including unsaved objects and empty results.
-
Refactor
- Centralized trigger handling for find and get operations to ensure consistent execution and security enforcement.
- Enhanced trigger processing with improved parameter handling, JSON serialization, and query construction for better robustness and clarity.
🚀 Thanks for opening this pull request!
📝 Walkthrough
Walkthrough
Centralizes beforeFind/afterFind handling; allows beforeFind to short‑circuit find/get by returning objects/arrays; propagates isGet through trigger requests and RestQuery; refilters returned objects to enforce ACLs before afterFind; updates trigger utilities and adds tests for these behaviors.
Changes
| Cohort / File(s) | Change Summary |
|---|---|
Testsspec/CloudCode.spec.js |
Added extensive tests for beforeFind short‑circuiting (single object, arrays, get, empty), ACL/protectedFields scenarios, and direct maybeRunAfterFindTrigger edge cases. |
REST trigger orchestrationsrc/rest.js |
Added internal runFindTriggers to centralize beforeFind/afterFind for find/get; supports beforeFind returning objects/arrays, performs refilter-by-ID query (with triggers disabled) to enforce ACLs, then runs afterFind; find/get now enforce role security and delegate to runFindTriggers. |
Trigger utilitiessrc/triggers.js |
toJSONwithObjects preserves className; getRequestObject(...) and maybeRun* functions accept isGet; maybeRunAfterFindTrigger coerces queries/objects into Parse.Query/Parse.Object, validates classNameQuery, normalizes inputs, improves logging, and returns JSON results. |
Query layersrc/RestQuery.js |
Introduced boolean isGet propagated into _UnsafeRestQuery and used instead of repeated method checks; isGet passed to trigger-related calls (e.g., maybeRunQueryTrigger, runAfterFindTrigger). |
Sequence Diagram(s)
sequenceDiagram
participant Client
participant REST as REST Handler
participant Triggers
participant DB
Client->>REST: find/get request
REST->>Triggers: beforeFind(request { isGet, query, auth, ... })
alt beforeFind returns objects/array
Triggers-->>REST: objects/array
REST->>REST: refilter by IDs (no triggers) to enforce ACLs
REST->>Triggers: afterFind(request { isGet, query, auth, ... }, results)
Triggers-->>REST: processedResults
REST-->>Client: processedResults
else
Triggers-->>REST: (maybe modified) query/options
REST->>DB: execute query (find/get)
DB-->>REST: results
REST->>Triggers: afterFind(request { isGet, query, auth, ... }, results)
Triggers-->>REST: finalResults
REST-->>Client: finalResults
end
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes
Assessment against linked issues
| Objective | Addressed | Explanation |
|---|---|---|
| Allow beforeFind to return objects, bypassing RestQuery (#8693) | ✅ | beforeFind can return objects/arrays; runFindTriggers handles the short‑circuit path. |
| Support returning arrays or empty array from beforeFind to short‑circuit queries (#8693) | ✅ | Arrays and empty arrays are supported and tested. |
| Ensure afterFind receives objects returned from beforeFind short‑circuiting (#8693) | ✅ | Returned objects are re-filtered for ACLs then passed to afterFind. |
| Add tests covering beforeFind short‑circuiting and maybeRunAfterFindTrigger behavior (#8693) | ✅ | New test suites added in spec/CloudCode.spec.js. |
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Linked Issues Check | ✅ Passed | The changes introduce centralized beforeFind handling that respects returned object arrays or empty arrays to bypass the default RestQuery flow, re-filters results for ACL compliance, and runs afterFind triggers as specified in issue #8693. |
| Out of Scope Changes Check | ✅ Passed | All additions and signature adjustments in rest.js, triggers.js, and RestQuery.js directly support the beforeFind short-circuit feature—including centralized runFindTriggers logic, isGet propagation, and trigger invocation changes—and there are no unrelated or extraneous modifications outside that objective. |
| Description Check | ✅ Passed | The description accurately follows the repository template by including the Pull Request header with security and licensing links, a filled-in Issue section referencing the specific linked issue, a concise Approach section summarizing the changes, and a trimmed Tasks list reflecting only the relevant test addition. |
| Title Check | ✅ Passed | The title clearly and concisely summarizes the primary change by describing the new ability to return objects from Parse.Cloud.beforeFind and bypass the database query, directly reflecting the pull request’s main feature and objectives. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
:white_check_mark: Snyk checks have passed. No issues have been found so far.
| Status | Scanner | Total (0) | ||||
|---|---|---|---|---|---|---|
| :white_check_mark: | Open Source Security | 0 | 0 | 0 | 0 | 0 issues |
:computer: Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.
Started CI...
Hi @mtrezza, just wondering if there's anything else I should do to trigger a re-run of the tests?
@EmpiDev You don't have permission to trigger a CI run. Once your first PR has been merged, the CI will run automatically.
Hi @mtrezza,
I'm still encountering some test failures when running npm test.
On the feat/before-found branch, 3 tests are failing:
BRANCH: FEAT BEFORE FOUND
3300 specs, 3 failures, 73 pending specs
Finished in 382.402 seconds
Randomized with seed 83016
Failures:
1) httpRequest should fail gracefully
- Timeout - Async function did not complete within 10000ms
2) Server Url Checks does not have unhandled promise rejection in the case of load error
- Timeout - Async function did not complete within 10000ms
3) Non-spec failure
- Spec "httpRequest should fail gracefully" ran a "toBeRejected" expectation after it finished.
→ Did you forget to `return` or `await` the result of `expectAsync`?
→ Was `done()` invoked before an async operation completed?
→ Did an expectation follow a call to `done()`?
I noticed that these same tests are also failing on the alpha branch.
Is that expected behavior, or should they pass there?
Thanks in advance for your help! Let me know if you want me to dig further.
These may be flaky tests. I'd have to re-run the CI to see whether they pass. If a test passes locally on your machine but fails in the CI with a timeout, then it's likely flaky. I've restarted the CI.
Codecov Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 93.04%. Comparing base (1b23475) to head (852f29f).
:warning: Report is 21 commits behind head on alpha.
Additional details and impacted files
@@ Coverage Diff @@
## alpha #9770 +/- ##
==========================================
+ Coverage 93.00% 93.04% +0.03%
==========================================
Files 187 187
Lines 15105 15147 +42
Branches 174 174
==========================================
+ Hits 14048 14093 +45
+ Misses 1045 1042 -3
Partials 12 12
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
@Moumouls would you kindly review the changes made after your feedback and close the feedback that you see as addressed?
Started CI
everything seems okay here @mtrezza :)
Merged alpha branch to run CI again;
@EmpiDev could you please resolve the lint issues? @Moumouls could you please resolve any open comments - there are some hidden in the thread.
@mtrezza i don't see some unresolved comments, or i can't resolve them because file are outdated or i approved the PR already
@mtrezza I have resolved the lint issues.
thanks @mtrezza , i reviewed, there is still one open comment: https://github.com/parse-community/parse-server/pull/9770#discussion_r2388521470
Waiting for CI...
🎉 This change has been released in version 8.3.0-alpha.5
🎉 This change has been released in version 8.3.0