Description
New Issue Checklist
- I am not disclosing a vulnerability.
- I am not just asking a question.
- I have searched through existing issues.
- I can reproduce the issue with the latest version of Parse Server.
Issue Description
The keys
and excludeKeys
on the server are not digesting actual JSON arrays, ["yolo",...], but instead digesting "yolo,..."
JS treats both of these the same (I discuss why below), but the server doesn't like it the way the Swift JSON encoder sends an encoded array for these properties, ["yolo",...]
. Note that this is the JSON encoder straight from Apples Swift source code and not the custom one used here for ParseObjects, so if it was encoding arrays incorrectly I'm sure it would have been caught awhile ago as the Apple JSON encoder is used by a ton of people.
At first glance this may appear to be a client side bug as there are currently server-side tests and the JS SDK(select and exclude), Flutter SDK (keysToReturn and excludeKeysToReturn), and the Android SDK (selectedKeys) uses it. The iOS SDK uses selectedKeys
, but never sends them to the server, instead it strips the keys not selected after the server returns the ParseObject
, so I'll ignore this SDK for the rest of the discussion. Below is why I believe the above SDKs work, but also why their current design (which can remain the same) has led to the problem not being discovered until now:
- The JS, Flutter, and Android SDKs don't actually encode JSON arrays for
select
andexclude
. Instead, they all join array elements into astring
separated by commas before sending to the server, always sending something like,"yolo,..."
which isn't an JSON array. This is why their implementations work with the current Parse Server implementation. Conversely, the Parse-Swift SDK uses Apples native JSON encoder and sends["yolo",...]
. When I make the Parse-Swift use a join array like the aforementioned SDKs, it works fine with the current implementation on the server. - The changes in the PR doesn't break any of the parse-server test cases which means the JS, Flutter, and Android SDKs will probably not require any changes. If they do end up requiring changes, then it will be a change for the better because they should have originally been sending JSON arrays. Instead, the JS, Flutter, and Android SDKs have essentially adapted to a bug. Example from the REST documentation for sending arrays (notice the
"$all":[2,3,4]
):
curl -X GET \
-H "X-Parse-Application-Id: ${APPLICATION_ID}" \
-H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
-G \
--data-urlencode 'where={"arrayKey":{"$all":[2,3,4]}}' \
https://YOUR.PARSE-SERVER.HERE/parse/classes/RandomObject
- The REST documentation for query constraints highlights the current problem with
keys
andexcludeKeys
by stating:
You can restrict the fields returned by passing keys or excludeKeys a comma-separated list.
IMO this deviates from purpose of REST and JSON as it suggests manually parsing/creating lists/strings instead of using underlying libraries that encode/decode JSON based on standards. I’ll create a PR to fix this as well
The linked PR has the possibility of being breaking change for some users due to:
This is the test case I removed because it 1) doesn't work anymore 2) not sure why we want this particular functionality for(I have sense added back in this feature in the PR, but my thoughts on this are below)select
. If you really want to exclude all keys, there should probably create anexcludeAll
or anexclude(["*"]
I didn't attempt to make that old test case work anymore, because I don't think the feature should have been there in the first place, but I'm open to discussion on this.
Approach
Make keys
and excludeKeys
use the same strategy as a similar property like include
which actually digests a JSON string array,
parse-server/src/Routers/ClassesRouter.js
Lines 60 to 68 in b4920a6
Steps to reproduce
- Make your favorite client SDK take an array (["yolo", ...]) of
string
forkeys
orexcludeKeys
. You can use the links for each SDK above. You can see an example when the issue was first discovered on Parse-Swift, .select() in finds and firsts does not seem to exclude any fields Parse-Swift#86 - Create a
select
orexcludeKey
query - Use first or find to query the server
Actual Outcome
All keys will be returned as the server ignores the selected or excludedKeys
Expected Outcome
The server should honor what ever keys you select/exclude in an JSON array
Failing Test Case / Pull Request
- 🤩 I submitted a PR with a fix and a test case.
- 🧐 I submitted a PR with a failing test case.
Not possible on the server side because the test cases use the JS SDK and I mentioned above JS doesn’t actually send JSON arrays forkeys
andexcludeKeys
, instead it joins the array into a comma delineated string on the client side, essentially removing the fact that it’s an array. To write this test, you would have to make the JS SDK send a real array, have that PR approved, bump JS on the server, etc.
Environment
Server
- Parse Server version:
master
,4.5.0
- Operating system:
Docker linux
,macOS
- Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc):
local
Database
- System (MongoDB or Postgres):
mongo
,postgres
- Database version:
4.4
,13.2
- Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc):
local
Client
- SDK (iOS, Android, JavaScript, PHP, Unity, etc):
Parse-Swift
- SDK version:
1.1.0