Compare commits

..
93 Commits
Author SHA1 Message Date
semantic-release-bot ed0baa87af chore(release): 5.2.2 [skip ci]
## [5.2.2](https://github.com/parse-community/parse-server/compare/5.2.1...5.2.2) (2022-06-17)

### Bug Fixes

* certificate in Apple Game Center auth adapter not validated; this fixes a security vulnerability in which authentication could be bypassed using a fake certificate; if you are using the Apple Gamer Center auth adapter it is your responsibility to keep its root certificate up-to-date and we advice you read the security advisory ([GHSA-rh9j-f5f8-rvgc](https://github.com/parse-community/parse-server/security/advisories/GHSA-rh9j-f5f8-rvgc)) ([ba2b0a9](https://github.com/parse-community/parse-server/commit/ba2b0a9cb9a568817a114b132a4c2e0911d76df1))
2022-06-17 16:36:47 +00:00
Manuel ba2b0a9cb9 fix: certificate in Apple Game Center auth adapter not validated; this fixes a security vulnerability in which authentication could be bypassed using a fake certificate; if you are using the Apple Gamer Center auth adapter it is your responsibility to keep its root certificate up-to-date and we advice you read the security advisory ([GHSA-rh9j-f5f8-rvgc](https://github.com/parse-community/parse-server/security/advisories/GHSA-rh9j-f5f8-rvgc)) 2022-06-17 18:29:26 +02:00
semantic-release-bot a8aef820af chore(release): 5.2.1 [skip ci]
## [5.2.1](https://github.com/parse-community/parse-server/compare/5.2.0...5.2.1) (2022-05-01)

### Bug Fixes

* authentication bypass and denial of service (DoS) vulnerabilities in Apple Game Center auth adapter (GHSA-qf8x-vqjv-92gr) ([#7962](https://github.com/parse-community/parse-server/issues/7962)) ([af4a041](https://github.com/parse-community/parse-server/commit/af4a0417a9f3c1e99b3793806b4b18e04d9fa999))
2022-05-01 00:31:48 +00:00
Manuel af4a0417a9 fix: authentication bypass and denial of service (DoS) vulnerabilities in Apple Game Center auth adapter (GHSA-qf8x-vqjv-92gr) (#7962) 2022-05-01 02:28:16 +02:00
semantic-release-bot 852bb4782a chore(release): 5.2.0 [skip ci]
# [5.2.0](https://github.com/parse-community/parse-server/compare/5.1.1...5.2.0) (2022-03-24)

### Bug Fixes

* security bump minimist from 1.2.5 to 1.2.6 ([#7884](https://github.com/parse-community/parse-server/issues/7884)) ([c5cf282](https://github.com/parse-community/parse-server/commit/c5cf282d11ffdc023764f8e7539a2bd6bc246fe1))
* sensitive keyword detection may produce false positives ([#7881](https://github.com/parse-community/parse-server/issues/7881)) ([0d6f9e9](https://github.com/parse-community/parse-server/commit/0d6f9e951d9e186e95e96d8869066ce7022bad02))

### Features

* improved LiveQuery error logging with additional information ([#7837](https://github.com/parse-community/parse-server/issues/7837)) ([443a509](https://github.com/parse-community/parse-server/commit/443a5099059538d379fe491793a5871fcbb4f377))
2022-03-24 18:23:57 +00:00
Manuel 9c414804ac build: release 2022-03-24 19:18:30 +01:00
mtrezza ff5b39123b ci: release commit 2022-03-24 18:16:15 +00:00
semantic-release-bot 2aeae76d80 chore(release): 5.2.0-beta.2 [skip ci]
# [5.2.0-beta.2](https://github.com/parse-community/parse-server/compare/5.2.0-beta.1...5.2.0-beta.2) (2022-03-24)

### Bug Fixes

* security bump minimist from 1.2.5 to 1.2.6 ([#7884](https://github.com/parse-community/parse-server/issues/7884)) ([c5cf282](https://github.com/parse-community/parse-server/commit/c5cf282d11ffdc023764f8e7539a2bd6bc246fe1))
* sensitive keyword detection may produce false positives ([#7881](https://github.com/parse-community/parse-server/issues/7881)) ([0d6f9e9](https://github.com/parse-community/parse-server/commit/0d6f9e951d9e186e95e96d8869066ce7022bad02))
2022-03-24 18:13:50 +00:00
Manuel 065141f00e build: release beta 2022-03-24 19:12:47 +01:00
mtrezza 0575952509 ci: release commit 2022-03-24 18:12:13 +00:00
semantic-release-bot bc07a3ff3a chore(release): 5.2.0-alpha.3 [skip ci]
# [5.2.0-alpha.3](https://github.com/parse-community/parse-server/compare/5.2.0-alpha.2...5.2.0-alpha.3) (2022-03-24)

### Bug Fixes

* security bump minimist from 1.2.5 to 1.2.6 ([#7884](https://github.com/parse-community/parse-server/issues/7884)) ([c5cf282](https://github.com/parse-community/parse-server/commit/c5cf282d11ffdc023764f8e7539a2bd6bc246fe1))
2022-03-24 13:42:07 +00:00
dependabot[bot] c5cf282d11 fix: security bump minimist from 1.2.5 to 1.2.6 (#7884) 2022-03-24 14:41:04 +01:00
Manuel e2333ba262 docs: add release instructions to contribution guide (#7885) 2022-03-24 03:33:31 +01:00
semantic-release-bot 892c6f94d5 chore(release): 5.2.0-alpha.2 [skip ci]
# [5.2.0-alpha.2](https://github.com/parse-community/parse-server/compare/5.2.0-alpha.1...5.2.0-alpha.2) (2022-03-24)

### Bug Fixes

* sensitive keyword detection may produce false positives ([#7881](https://github.com/parse-community/parse-server/issues/7881)) ([0d6f9e9](https://github.com/parse-community/parse-server/commit/0d6f9e951d9e186e95e96d8869066ce7022bad02))
2022-03-24 01:55:04 +00:00
Manuel 0d6f9e951d fix: sensitive keyword detection may produce false positives (#7881) 2022-03-24 02:54:07 +01:00
semantic-release-bot 765cfd02dc chore(release): 5.2.0-beta.1 [skip ci]
# [5.2.0-beta.1](https://github.com/parse-community/parse-server/compare/5.1.1...5.2.0-beta.1) (2022-03-23)

### Features

* improved LiveQuery error logging with additional information ([#7837](https://github.com/parse-community/parse-server/issues/7837)) ([443a509](https://github.com/parse-community/parse-server/commit/443a5099059538d379fe491793a5871fcbb4f377))
2022-03-23 01:22:38 +00:00
Manuel ccd3d86137 build: release beta 2022-03-23 02:19:37 +01:00
mtrezza 693d72060d ci: release commit 2022-03-23 01:15:29 +00:00
semantic-release-bot 2b7f772a1b chore(release): 5.2.0-alpha.1 [skip ci]
# [5.2.0-alpha.1](https://github.com/parse-community/parse-server/compare/5.1.1...5.2.0-alpha.1) (2022-03-23)

### Features

* improved LiveQuery error logging with additional information ([#7837](https://github.com/parse-community/parse-server/issues/7837)) ([443a509](https://github.com/parse-community/parse-server/commit/443a5099059538d379fe491793a5871fcbb4f377))
2022-03-23 01:12:46 +00:00
dblythy 443a509905 feat: improved LiveQuery error logging with additional information (#7837) 2022-03-23 02:11:39 +01:00
Manuel 2830021990 build: release 2022-03-23 00:45:30 +01:00
Manuel d2d12d3d69 build: release 2022-03-23 00:39:13 +01:00
Manuel 1099ebbcd8 ci: fix backmerge credentials (#7876) 2022-03-23 00:26:36 +01:00
Manuel 4c50c4d11c ci: add backmerge branches (#7874) 2022-03-23 00:09:49 +01:00
Manuel e9d23830a3 docs: improve reverting in CONTRIBUTION guide (#7866) 2022-03-22 23:21:14 +01:00
semantic-release-bot 01096b360a chore(release): 5.1.1 [skip ci]
## [5.1.1](https://github.com/parse-community/parse-server/compare/5.1.0...5.1.1) (2022-03-18)

### Reverts

* ci: temporarily disable breaking change detection ([#7861](https://github.com/parse-community/parse-server/issues/7861)) ([effed92](https://github.com/parse-community/parse-server/commit/effed92cabd88676fdf9eca2e079a4d8be017f1b))
2022-03-18 15:14:57 +00:00
Manuel effed92cab revert: ci: temporarily disable breaking change detection (#7861)
This reverts commit ef9ee66640.
2022-03-18 16:13:29 +01:00
Manuel 9cdc6ca5b3 docs: update changelog (#7864) 2022-03-18 16:06:49 +01:00
semantic-release-bot 158a974c89 chore(release): 5.1.0 [skip ci]
# [5.1.0](https://github.com/parse-community/parse-server/compare/5.0.0...5.1.0) (2022-03-18)

### Bug Fixes

* adding or modifying a nested property requires addField permissions ([#7679](https://github.com/parse-community/parse-server/issues/7679)) ([6a6248b](https://github.com/parse-community/parse-server/commit/6a6248b6cb2e732d17131e18e659943b894ed2f1))
* bump nanoid from 3.1.25 to 3.2.0 ([#7781](https://github.com/parse-community/parse-server/issues/7781)) ([f5f63bf](https://github.com/parse-community/parse-server/commit/f5f63bfc64d3481ed944ceb5e9f50b33dccd1ce9))
* bump node-fetch from 2.6.1 to 3.1.1 ([#7782](https://github.com/parse-community/parse-server/issues/7782)) ([9082351](https://github.com/parse-community/parse-server/commit/90823514113a1a085ebc818f7109b3fd7591346f))
* node engine compatibility did not include node 16 ([#7739](https://github.com/parse-community/parse-server/issues/7739)) ([ea7c014](https://github.com/parse-community/parse-server/commit/ea7c01400f992a1263543706fe49b6174758a2d6))
* node engine range has no upper limit to exclude incompatible node versions ([#7692](https://github.com/parse-community/parse-server/issues/7692)) ([573558d](https://github.com/parse-community/parse-server/commit/573558d3adcbcc6222c92003829867e1a73eef94))
* package.json & package-lock.json to reduce vulnerabilities ([#7823](https://github.com/parse-community/parse-server/issues/7823)) ([5ca2288](https://github.com/parse-community/parse-server/commit/5ca228882332b65f3ac05407e6e4da1ee3ef3749))
* schema cache not cleared in some cases ([#7678](https://github.com/parse-community/parse-server/issues/7678)) ([5af6e5d](https://github.com/parse-community/parse-server/commit/5af6e5dfaa129b1a350afcba4fb381b21c4cc35d))
* security upgrade follow-redirects from 1.14.6 to 1.14.7 ([#7769](https://github.com/parse-community/parse-server/issues/7769)) ([8f5a861](https://github.com/parse-community/parse-server/commit/8f5a8618cfa7ed9a2a239a095abffa8f3fd8d31a))
* security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7801](https://github.com/parse-community/parse-server/issues/7801)) ([70088a9](https://github.com/parse-community/parse-server/commit/70088a95a78393da2a4ac68be81e63107747626a))
* security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) ([#7844](https://github.com/parse-community/parse-server/issues/7844)) ([e569f40](https://github.com/parse-community/parse-server/commit/e569f402b1fd8648fb0d1523b71b2a03273902a5))
* server crash using GraphQL due to missing @apollo/client peer dependency ([#7787](https://github.com/parse-community/parse-server/issues/7787)) ([08089d6](https://github.com/parse-community/parse-server/commit/08089d6fcbb215412448ce7d92b21b9fe6c929f2))
* unable to use objectId size higher than 19 on GraphQL API ([#7627](https://github.com/parse-community/parse-server/issues/7627)) ([ed86c80](https://github.com/parse-community/parse-server/commit/ed86c807721cc52a1a5a9dea0b768717eec269ed))
* upgrade mime from 2.5.2 to 3.0.0 ([#7725](https://github.com/parse-community/parse-server/issues/7725)) ([f5ef98b](https://github.com/parse-community/parse-server/commit/f5ef98bde32083403c0e30a12162fcc1e52cac37))
* upgrade parse from 3.3.1 to 3.4.0 ([#7723](https://github.com/parse-community/parse-server/issues/7723)) ([d4c1f47](https://github.com/parse-community/parse-server/commit/d4c1f473073764cb0570c633fc4a30669c2ce889))
* upgrade winston from 3.5.0 to 3.5.1 ([#7820](https://github.com/parse-community/parse-server/issues/7820)) ([4af253d](https://github.com/parse-community/parse-server/commit/4af253d1f8654a6f57b5137ad310cdacadc922cc))

### Features

* add Cloud Code context to `ParseObject.fetch` ([#7779](https://github.com/parse-community/parse-server/issues/7779)) ([315290d](https://github.com/parse-community/parse-server/commit/315290d16110110938f80a6b779cc2d1db58c552))
* add Idempotency to Postgres ([#7750](https://github.com/parse-community/parse-server/issues/7750)) ([0c3feaa](https://github.com/parse-community/parse-server/commit/0c3feaaa1751964c0db89f25674935c3354b1538))
* add support for Node 16 ([#7707](https://github.com/parse-community/parse-server/issues/7707)) ([45cc58c](https://github.com/parse-community/parse-server/commit/45cc58c7e5e640a46c5d508019a3aa81242964b1))
* bump required node engine to >=12.22.10 ([#7846](https://github.com/parse-community/parse-server/issues/7846)) ([5ace99d](https://github.com/parse-community/parse-server/commit/5ace99d542a11e422af46d9fd6b1d3d2513b34cf))
* support `postgresql` protocol in database URI ([#7757](https://github.com/parse-community/parse-server/issues/7757)) ([caf4a23](https://github.com/parse-community/parse-server/commit/caf4a2341f554b28e3918c53e7e897a3ca47bf8b))
* support relativeTime query constraint on Postgres ([#7747](https://github.com/parse-community/parse-server/issues/7747)) ([16b1b2a](https://github.com/parse-community/parse-server/commit/16b1b2a19714535ca805f2dbb3b561d8f6a519a7))
* upgrade to MongoDB Node.js driver 4.x for MongoDB 5.0 support ([#7794](https://github.com/parse-community/parse-server/issues/7794)) ([f88aa2a](https://github.com/parse-community/parse-server/commit/f88aa2a62a533e5344d1c13dd38c5a0b283a480a))

### Reverts

* refactor: allow ES import for cloud string if package type is module ([b64640c](https://github.com/parse-community/parse-server/commit/b64640c5705f733798783e68d216e957044ef23c))
* update node engine to 2.22.0 ([#7827](https://github.com/parse-community/parse-server/issues/7827)) ([f235412](https://github.com/parse-community/parse-server/commit/f235412c1b6c2b173b7531f285429ea7214b56a2))
2022-03-18 14:48:03 +00:00
Manuel Trezza 1593575a87 build: release 2022-03-18 15:17:12 +01:00
Manuel ef9ee66640 ci: temporarily disable breaking change detection (#7861) 2022-03-18 15:02:41 +01:00
Manuel d5db318db8 ci: temporarily disable braking change detection (#7860) 2022-03-18 14:38:52 +01:00
semantic-release-bot 9fa80eb4ed chore(release): 5.0.0-beta.10 [skip ci]
# [5.0.0-beta.10](https://github.com/parse-community/parse-server/compare/5.0.0-beta.9...5.0.0-beta.10) (2022-03-15)

### Bug Fixes

* adding or modifying a nested property requires addField permissions ([#7679](https://github.com/parse-community/parse-server/issues/7679)) ([6a6248b](https://github.com/parse-community/parse-server/commit/6a6248b6cb2e732d17131e18e659943b894ed2f1))
* bump nanoid from 3.1.25 to 3.2.0 ([#7781](https://github.com/parse-community/parse-server/issues/7781)) ([f5f63bf](https://github.com/parse-community/parse-server/commit/f5f63bfc64d3481ed944ceb5e9f50b33dccd1ce9))
* bump node-fetch from 2.6.1 to 3.1.1 ([#7782](https://github.com/parse-community/parse-server/issues/7782)) ([9082351](https://github.com/parse-community/parse-server/commit/90823514113a1a085ebc818f7109b3fd7591346f))
* node engine compatibility did not include node 16 ([#7739](https://github.com/parse-community/parse-server/issues/7739)) ([ea7c014](https://github.com/parse-community/parse-server/commit/ea7c01400f992a1263543706fe49b6174758a2d6))
* node engine range has no upper limit to exclude incompatible node versions ([#7692](https://github.com/parse-community/parse-server/issues/7692)) ([573558d](https://github.com/parse-community/parse-server/commit/573558d3adcbcc6222c92003829867e1a73eef94))
* package.json & package-lock.json to reduce vulnerabilities ([#7823](https://github.com/parse-community/parse-server/issues/7823)) ([5ca2288](https://github.com/parse-community/parse-server/commit/5ca228882332b65f3ac05407e6e4da1ee3ef3749))
* schema cache not cleared in some cases ([#7678](https://github.com/parse-community/parse-server/issues/7678)) ([5af6e5d](https://github.com/parse-community/parse-server/commit/5af6e5dfaa129b1a350afcba4fb381b21c4cc35d))
* security upgrade follow-redirects from 1.14.6 to 1.14.7 ([#7769](https://github.com/parse-community/parse-server/issues/7769)) ([8f5a861](https://github.com/parse-community/parse-server/commit/8f5a8618cfa7ed9a2a239a095abffa8f3fd8d31a))
* security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7801](https://github.com/parse-community/parse-server/issues/7801)) ([70088a9](https://github.com/parse-community/parse-server/commit/70088a95a78393da2a4ac68be81e63107747626a))
* security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) ([#7844](https://github.com/parse-community/parse-server/issues/7844)) ([e569f40](https://github.com/parse-community/parse-server/commit/e569f402b1fd8648fb0d1523b71b2a03273902a5))
* server crash using GraphQL due to missing @apollo/client peer dependency ([#7787](https://github.com/parse-community/parse-server/issues/7787)) ([08089d6](https://github.com/parse-community/parse-server/commit/08089d6fcbb215412448ce7d92b21b9fe6c929f2))
* unable to use objectId size higher than 19 on GraphQL API ([#7627](https://github.com/parse-community/parse-server/issues/7627)) ([ed86c80](https://github.com/parse-community/parse-server/commit/ed86c807721cc52a1a5a9dea0b768717eec269ed))
* upgrade mime from 2.5.2 to 3.0.0 ([#7725](https://github.com/parse-community/parse-server/issues/7725)) ([f5ef98b](https://github.com/parse-community/parse-server/commit/f5ef98bde32083403c0e30a12162fcc1e52cac37))
* upgrade parse from 3.3.1 to 3.4.0 ([#7723](https://github.com/parse-community/parse-server/issues/7723)) ([d4c1f47](https://github.com/parse-community/parse-server/commit/d4c1f473073764cb0570c633fc4a30669c2ce889))
* upgrade winston from 3.5.0 to 3.5.1 ([#7820](https://github.com/parse-community/parse-server/issues/7820)) ([4af253d](https://github.com/parse-community/parse-server/commit/4af253d1f8654a6f57b5137ad310cdacadc922cc))

### Features

* add Cloud Code context to `ParseObject.fetch` ([#7779](https://github.com/parse-community/parse-server/issues/7779)) ([315290d](https://github.com/parse-community/parse-server/commit/315290d16110110938f80a6b779cc2d1db58c552))
* add Idempotency to Postgres ([#7750](https://github.com/parse-community/parse-server/issues/7750)) ([0c3feaa](https://github.com/parse-community/parse-server/commit/0c3feaaa1751964c0db89f25674935c3354b1538))
* add support for Node 16 ([#7707](https://github.com/parse-community/parse-server/issues/7707)) ([45cc58c](https://github.com/parse-community/parse-server/commit/45cc58c7e5e640a46c5d508019a3aa81242964b1))
* bump required node engine to >=12.22.10 ([#7846](https://github.com/parse-community/parse-server/issues/7846)) ([5ace99d](https://github.com/parse-community/parse-server/commit/5ace99d542a11e422af46d9fd6b1d3d2513b34cf))
* support `postgresql` protocol in database URI ([#7757](https://github.com/parse-community/parse-server/issues/7757)) ([caf4a23](https://github.com/parse-community/parse-server/commit/caf4a2341f554b28e3918c53e7e897a3ca47bf8b))
* support relativeTime query constraint on Postgres ([#7747](https://github.com/parse-community/parse-server/issues/7747)) ([16b1b2a](https://github.com/parse-community/parse-server/commit/16b1b2a19714535ca805f2dbb3b561d8f6a519a7))
* upgrade to MongoDB Node.js driver 4.x for MongoDB 5.0 support ([#7794](https://github.com/parse-community/parse-server/issues/7794)) ([f88aa2a](https://github.com/parse-community/parse-server/commit/f88aa2a62a533e5344d1c13dd38c5a0b283a480a))

### Reverts

* refactor: allow ES import for cloud string if package type is module ([b64640c](https://github.com/parse-community/parse-server/commit/b64640c5705f733798783e68d216e957044ef23c))
* update node engine to 2.22.0 ([#7827](https://github.com/parse-community/parse-server/issues/7827)) ([f235412](https://github.com/parse-community/parse-server/commit/f235412c1b6c2b173b7531f285429ea7214b56a2))

### BREAKING CHANGES

* This requires Node.js version >=12.22.10. ([5ace99d](5ace99d))
* The MongoDB GridStore adapter has been removed. By default, Parse Server already uses GridFS, so if you do not manually use the GridStore adapter, you can ignore this change. ([f88aa2a](f88aa2a))
* Removes official Node 15 support which has reached it end-of-life date. ([45cc58c](45cc58c))
2022-03-15 00:13:57 +00:00
Manuel ff16839450 build: release beta 2022-03-15 01:12:57 +01:00
Manuel 0c1b75fcbe Merge branch 'beta' into build-release-beta-19837863611 2022-03-15 00:56:54 +01:00
mtrezza 69781ce0cc ci: release commit 2022-03-14 23:51:23 +00:00
Manuel 2eebc68f21 docs: fix release link in CHANGELOG (#7856) 2022-03-14 18:20:12 +01:00
Manuel 79e00e1dee docs: update changelog (#7855) 2022-03-14 13:57:39 +01:00
semantic-release-bot 46c9a91627 chore(release): 5.0.0 [skip ci]
# [5.0.0](https://github.com/parse-community/parse-server/compare/4.5.0...5.0.0) (2022-03-14)

### Bug Fixes

* add deprecation warning for `Parse.Cloud.httpRequest` ([#7595](https://github.com/parse-community/parse-server/issues/7595)) ([ab1dddd](https://github.com/parse-community/parse-server/commit/ab1dddd406589800e1a8ebe8381b9da702e9c641))
* add support for descending sorting of full text search ([#7496](https://github.com/parse-community/parse-server/issues/7496)) ([8ed9442](https://github.com/parse-community/parse-server/commit/8ed94421e69b1d4fde4ec3625a1f4cfbd6d39c2b))
* allow LiveQuery on Parse.Session ([#7554](https://github.com/parse-community/parse-server/issues/7554)) ([caee281](https://github.com/parse-community/parse-server/commit/caee281bc58901fea7cead21d6a9a0bb2022d91b))
* combined `and` query with relational query condition returns incorrect results ([#7593](https://github.com/parse-community/parse-server/issues/7593)) ([174886e](https://github.com/parse-community/parse-server/commit/174886e385e091c6bbd4a84891ef95f80b50d05c))
* empty file tags cause upload error for some providers ([#7300](https://github.com/parse-community/parse-server/issues/7300)) ([4d16702](https://github.com/parse-community/parse-server/commit/4d167026aebed38bf3a3b74599ab3771d1da3245))
* **Logger:** Handle interpolating stdout ([#7114](https://github.com/parse-community/parse-server/issues/7114)) ([1ede078](https://github.com/parse-community/parse-server/commit/1ede078154213d3426cc492612ce41bdb1eda43b))
* improve security by deprecating creating users with public access by default ([#7319](https://github.com/parse-community/parse-server/issues/7319)) ([484c2e8](https://github.com/parse-community/parse-server/commit/484c2e81cace608eb28508a0926b17b3ba9e6233))
* node engine range has no upper limit to exclude incompatible node versions ([#7693](https://github.com/parse-community/parse-server/issues/7693)) ([6a54dac](https://github.com/parse-community/parse-server/commit/6a54dac24d9fb63a44f311b8d414f4aa64140f32))
* package.json & package-lock.json to reduce vulnerabilities ([#7112](https://github.com/parse-community/parse-server/issues/7112)) ([7b8d8dd](https://github.com/parse-community/parse-server/commit/7b8d8ddbb0de583621a4d2ede95793c6516bc4c0))
* package.json & package-lock.json to reduce vulnerabilities ([#7218](https://github.com/parse-community/parse-server/issues/7218)) ([0476832](https://github.com/parse-community/parse-server/commit/047683219daf7940590db1237f6826a038bdc41d))
* package.json & package-lock.json to reduce vulnerabilities ([#7373](https://github.com/parse-community/parse-server/issues/7373)) ([b6843de](https://github.com/parse-community/parse-server/commit/b6843de904bf273bf664d94536defa39f91751b3))
* package.json & package-lock.json to reduce vulnerabilities ([#7405](https://github.com/parse-community/parse-server/issues/7405)) ([d915bac](https://github.com/parse-community/parse-server/commit/d915bacee7c6ded60b303582d2a6b85d42dc3cf4))
* package.json & package-lock.json to reduce vulnerabilities ([#7423](https://github.com/parse-community/parse-server/issues/7423)) ([bea4707](https://github.com/parse-community/parse-server/commit/bea4707783febc0fc6ea055835ffbc517cd2b067))
* package.json & package-lock.json to reduce vulnerabilities ([#7509](https://github.com/parse-community/parse-server/issues/7509)) ([65c967a](https://github.com/parse-community/parse-server/commit/65c967a4c446aebb535cbbbcb0ceb38d45da7859))
* Pass customObjectId in beforeSave ([#7167](https://github.com/parse-community/parse-server/issues/7167)) ([7224cde](https://github.com/parse-community/parse-server/commit/7224cde0232d79fea0f4b2d0bc2cb7641ec467d8)), closes [#6733](https://github.com/parse-community/parse-server/issues/6733)
* **utils:** permutation helper ([#7355](https://github.com/parse-community/parse-server/issues/7355)) ([91be6bb](https://github.com/parse-community/parse-server/commit/91be6bb59a61c126b7a25431655064342464905c))
* schema cache not cleared in some cases ([#7771](https://github.com/parse-community/parse-server/issues/7771)) ([3b92fa1](https://github.com/parse-community/parse-server/commit/3b92fa1ca9e8889127a32eba913d68309397ca2c))
* security upgrade follow-redirects from 1.14.2 to 1.14.7 ([#7772](https://github.com/parse-community/parse-server/issues/7772)) ([4bd34b1](https://github.com/parse-community/parse-server/commit/4bd34b189bc9f5aa2e70b7e7c1a456e91b6de773))
* security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7802](https://github.com/parse-community/parse-server/issues/7802)) ([7029b27](https://github.com/parse-community/parse-server/commit/7029b274ca87bc8058617f29865d683dc3b351a1))
* security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) ([#7843](https://github.com/parse-community/parse-server/issues/7843)) ([971adb5](https://github.com/parse-community/parse-server/commit/971adb54387b0ede31be05ca407d5f35b4575c83))
* set objects in afterFind triggers ([#7311](https://github.com/parse-community/parse-server/issues/7311)) ([68a3a87](https://github.com/parse-community/parse-server/commit/68a3a875017d545253f0438c5d4c1c2b6c6c3b22))
* setting a field to null does not delete it via GraphQL API ([#7649](https://github.com/parse-community/parse-server/issues/7649)) ([626fad2](https://github.com/parse-community/parse-server/commit/626fad2e71017dcc62196c487de5f908fa43000b))
* unable to use objectId size higher than 19 on GraphQL API ([#7722](https://github.com/parse-community/parse-server/issues/7722)) ([8ee0445](https://github.com/parse-community/parse-server/commit/8ee0445c0aeeb88dff2559b46ade408071d22143))
* upgrade @apollographql/graphql-playground-html from 1.6.26 to 1.6.27 ([#7274](https://github.com/parse-community/parse-server/issues/7274)) ([a05e9b1](https://github.com/parse-community/parse-server/commit/a05e9b1c0fee8ad917812637256faa1e0a1b40f6))
* upgrade @apollographql/graphql-playground-html from 1.6.27 to 1.6.28 ([#7411](https://github.com/parse-community/parse-server/issues/7411)) ([c58bf57](https://github.com/parse-community/parse-server/commit/c58bf57f51ebc9df3a624fb3ecdacdf4647f1dea))
* upgrade @apollographql/graphql-playground-html from 1.6.28 to 1.6.29 ([#7473](https://github.com/parse-community/parse-server/issues/7473)) ([39f7c83](https://github.com/parse-community/parse-server/commit/39f7c831e6493cff9cd64195b64e886fda4624f8))
* upgrade @parse/simple-mailgun-adapter from 1.1.0 to 1.2.0 ([#7109](https://github.com/parse-community/parse-server/issues/7109)) ([8ff0d08](https://github.com/parse-community/parse-server/commit/8ff0d08dcf5a9ada931359df3e78543445312f15))
* upgrade apollo-server-express from 2.19.0 to 2.19.1 ([#7122](https://github.com/parse-community/parse-server/issues/7122)) ([33bdd87](https://github.com/parse-community/parse-server/commit/33bdd8709266f17489c319e43b3f2a7d8ea27fd5))
* upgrade apollo-server-express from 2.19.1 to 2.19.2 ([#7165](https://github.com/parse-community/parse-server/issues/7165)) ([4b6e9ff](https://github.com/parse-community/parse-server/commit/4b6e9ffc3d2c1969c285c94f7cf2cfc7af03227e))
* upgrade apollo-server-express from 2.19.2 to 2.20.0 ([#7239](https://github.com/parse-community/parse-server/issues/7239)) ([d10e990](https://github.com/parse-community/parse-server/commit/d10e99007b70816707944060905cff7da5018eb0))
* upgrade apollo-server-express from 2.21.0 to 2.21.1 ([#7308](https://github.com/parse-community/parse-server/issues/7308)) ([3dc4597](https://github.com/parse-community/parse-server/commit/3dc459746dbd4a2d245f2bde688cd087a5b74703))
* upgrade apollo-server-express from 2.21.1 to 2.22.1 ([#7357](https://github.com/parse-community/parse-server/issues/7357)) ([25690ad](https://github.com/parse-community/parse-server/commit/25690ad515551bda33dd581789ffeb9a218c8033))
* upgrade apollo-server-express from 2.22.1 to 2.22.2 ([#7362](https://github.com/parse-community/parse-server/issues/7362)) ([181fbf9](https://github.com/parse-community/parse-server/commit/181fbf9d46bee61b5c67a411b4a41c38d201cc73))
* upgrade apollo-server-express from 2.22.2 to 2.23.0 ([#7380](https://github.com/parse-community/parse-server/issues/7380)) ([87476da](https://github.com/parse-community/parse-server/commit/87476da31e28e064fa7192998c67de9c31ac18a6))
* upgrade apollo-server-express from 2.23.0 to 2.24.0 ([#7395](https://github.com/parse-community/parse-server/issues/7395)) ([ff5755b](https://github.com/parse-community/parse-server/commit/ff5755b05ac554a107a443a8870f4c2442b0a5f3))
* upgrade apollo-server-express from 2.24.0 to 2.24.1 ([#7424](https://github.com/parse-community/parse-server/issues/7424)) ([bfdb6a9](https://github.com/parse-community/parse-server/commit/bfdb6a93e8c0d78524820b32abaff174776520f3))
* upgrade apollo-server-express from 2.24.1 to 2.25.0 ([#7435](https://github.com/parse-community/parse-server/issues/7435)) ([4e5eba6](https://github.com/parse-community/parse-server/commit/4e5eba6c6c7bc6e643ac6477df15beea1ba60364))
* upgrade apollo-server-express from 2.25.0 to 2.25.1 ([#7449](https://github.com/parse-community/parse-server/issues/7449)) ([682f1bf](https://github.com/parse-community/parse-server/commit/682f1bf14388e134fbd993e274f5738ec2e57e02))
* upgrade apollo-server-express from 2.25.1 to 2.25.2 ([#7465](https://github.com/parse-community/parse-server/issues/7465)) ([1fe4708](https://github.com/parse-community/parse-server/commit/1fe47087a92c80e7ee8cc6e3e26b3a67f8df68ee))
* upgrade follow-redirects from 1.13.0 to 1.13.1 ([#7106](https://github.com/parse-community/parse-server/issues/7106)) ([16b4aad](https://github.com/parse-community/parse-server/commit/16b4aadfe2b5b5b24e6e3bbe22c93a92f02c9eb3))
* upgrade follow-redirects from 1.13.1 to 1.13.2 ([#7194](https://github.com/parse-community/parse-server/issues/7194)) ([738ba9f](https://github.com/parse-community/parse-server/commit/738ba9fc704a770bd1e2af23bca31ed7202f99cd))
* upgrade follow-redirects from 1.13.2 to 1.13.3 ([#7285](https://github.com/parse-community/parse-server/issues/7285)) ([d144819](https://github.com/parse-community/parse-server/commit/d144819d21b59b11dc957346748ce3200349a6b7))
* upgrade follow-redirects from 1.13.3 to 1.14.0 ([#7389](https://github.com/parse-community/parse-server/issues/7389)) ([38c01c6](https://github.com/parse-community/parse-server/commit/38c01c6bc32fe769c3a636e45b389e2a995c2ee6))
* upgrade follow-redirects from 1.14.0 to 1.14.1 ([#7408](https://github.com/parse-community/parse-server/issues/7408)) ([8976ecc](https://github.com/parse-community/parse-server/commit/8976ecc4a9c91eeb1d0f3448eff7720b43c7747a))
* upgrade graphql from 15.4.0 to 15.5.0 ([#7201](https://github.com/parse-community/parse-server/issues/7201)) ([5a09687](https://github.com/parse-community/parse-server/commit/5a0968721d6d04cdde86d3df569a77910bcae803))
* upgrade graphql from 15.5.0 to 15.5.1 ([#7462](https://github.com/parse-community/parse-server/issues/7462)) ([bbd7ee7](https://github.com/parse-community/parse-server/commit/bbd7ee7313e19c43b15e16027439739590393dd1))
* upgrade graphql from 15.5.1 to 15.5.2 ([#7587](https://github.com/parse-community/parse-server/issues/7587)) ([dee4d96](https://github.com/parse-community/parse-server/commit/dee4d9662749eb699891c8a3797fa0b0467c6355))
* upgrade graphql from 15.5.2 to 15.5.3 ([#7596](https://github.com/parse-community/parse-server/issues/7596)) ([bcbc035](https://github.com/parse-community/parse-server/commit/bcbc035627c552a7940bec4ae210ca1522215246))
* upgrade graphql from 15.5.3 to 15.6.0 ([#7612](https://github.com/parse-community/parse-server/issues/7612)) ([407ed6e](https://github.com/parse-community/parse-server/commit/407ed6ee36ea42d7eb525f42b1f3da6f41230504))
* upgrade graphql-relay from 0.6.0 to 0.7.0 ([#7443](https://github.com/parse-community/parse-server/issues/7443)) ([770e36f](https://github.com/parse-community/parse-server/commit/770e36ff43c42e4ed3db73a30436079a0460a1f0))
* upgrade graphql-relay from 0.7.0 to 0.8.0 ([#7467](https://github.com/parse-community/parse-server/issues/7467)) ([9923cd3](https://github.com/parse-community/parse-server/commit/9923cd3869ab80e70c5ca9765628ba0a28a6facc))
* upgrade graphql-tag from 2.10.1 to 2.12.0 ([#7234](https://github.com/parse-community/parse-server/issues/7234)) ([add67fd](https://github.com/parse-community/parse-server/commit/add67fdd2284a58386f6afd35edeaa8534579e33))
* upgrade graphql-tag from 2.12.0 to 2.12.1 ([#7282](https://github.com/parse-community/parse-server/issues/7282)) ([36de1db](https://github.com/parse-community/parse-server/commit/36de1db65dc3c2bbf8f8051ee1862a06ef41e7f9))
* upgrade graphql-tag from 2.12.1 to 2.12.2 ([#7325](https://github.com/parse-community/parse-server/issues/7325)) ([50e5557](https://github.com/parse-community/parse-server/commit/50e55571fdaa1ac3ca43d215c4bc62136d684873))
* upgrade graphql-tag from 2.12.2 to 2.12.4 ([#7396](https://github.com/parse-community/parse-server/issues/7396)) ([8099cb0](https://github.com/parse-community/parse-server/commit/8099cb05a48956b073f3e7d96c30b69292c06adc))
* upgrade graphql-tag from 2.12.4 to 2.12.5 ([#7466](https://github.com/parse-community/parse-server/issues/7466)) ([2b3355c](https://github.com/parse-community/parse-server/commit/2b3355cb022724838499a5d83c0d532040953566))
* upgrade jwks-rsa from 1.11.0 to 1.12.0 ([#7102](https://github.com/parse-community/parse-server/issues/7102)) ([029edbf](https://github.com/parse-community/parse-server/commit/029edbf706c4b1754afbb5ce69600a373b91cb9b))
* upgrade jwks-rsa from 1.12.1 to 1.12.2 ([#7147](https://github.com/parse-community/parse-server/issues/7147)) ([bcb2b52](https://github.com/parse-community/parse-server/commit/bcb2b52f7a1322a780b95b77081827d6c8c25840))
* upgrade jwks-rsa from 1.12.2 to 1.12.3 ([#7284](https://github.com/parse-community/parse-server/issues/7284)) ([a53d74c](https://github.com/parse-community/parse-server/commit/a53d74c13b86d43e1694132feddccce6ee2bede9))
* upgrade ldapjs from 2.2.2 to 2.2.3 ([#7095](https://github.com/parse-community/parse-server/issues/7095)) ([fb465e5](https://github.com/parse-community/parse-server/commit/fb465e599e8768b9e229ec8689e033d7063e50b3))
* upgrade ldapjs from 2.2.3 to 2.2.4 ([#7275](https://github.com/parse-community/parse-server/issues/7275)) ([35f0c55](https://github.com/parse-community/parse-server/commit/35f0c55e93dc0572049a85befbc80175620f7d29))
* upgrade ldapjs from 2.2.4 to 2.3.0 ([#7436](https://github.com/parse-community/parse-server/issues/7436)) ([7df6c02](https://github.com/parse-community/parse-server/commit/7df6c020b18b8ea9d04b718fa4e070be800b9268))
* upgrade ldapjs from 2.3.0 to 2.3.1 ([#7524](https://github.com/parse-community/parse-server/issues/7524)) ([dee5a13](https://github.com/parse-community/parse-server/commit/dee5a13a85336cdd9d3bcd938c4118fffe8b9670))
* upgrade mime from 2.4.6 to 2.4.7 ([#7110](https://github.com/parse-community/parse-server/issues/7110)) ([fefcabe](https://github.com/parse-community/parse-server/commit/fefcabe858f3817dca337390c2198abbf12f9bdf))
* upgrade mime from 2.4.7 to 2.5.0 ([#7166](https://github.com/parse-community/parse-server/issues/7166)) ([6097e82](https://github.com/parse-community/parse-server/commit/6097e82194772847954dc2c2b7559543cc7531cc))
* upgrade mime from 2.5.0 to 2.5.2 ([#7261](https://github.com/parse-community/parse-server/issues/7261)) ([687f4b7](https://github.com/parse-community/parse-server/commit/687f4b7cf245ce578674a4cc0e0d276b23d5780d))
* upgrade mongodb from 3.6.6 to 3.6.7 ([#7425](https://github.com/parse-community/parse-server/issues/7425)) ([61affe2](https://github.com/parse-community/parse-server/commit/61affe26293f7218e88878d027514d68975cfc8c))
* upgrade mongodb from 3.6.7 to 3.6.8 ([#7430](https://github.com/parse-community/parse-server/issues/7430)) ([c36588e](https://github.com/parse-community/parse-server/commit/c36588e3c2d16fab3f38aecc77f0e827bda38f95))
* upgrade mongodb from 3.6.8 to 3.6.9 ([#7445](https://github.com/parse-community/parse-server/issues/7445)) ([17cf1a4](https://github.com/parse-community/parse-server/commit/17cf1a46e557b502b132c415b922508e032209a9))
* upgrade mongodb from 3.6.9 to 3.6.10 ([#7474](https://github.com/parse-community/parse-server/issues/7474)) ([45d29cc](https://github.com/parse-community/parse-server/commit/45d29cc58c373c9e1d489dbd7a750f2a0f6bc89b))
* upgrade mustache from 4.1.0 to 4.2.0 ([#7358](https://github.com/parse-community/parse-server/issues/7358)) ([94b7b32](https://github.com/parse-community/parse-server/commit/94b7b320062579e788df6c9f72d7a786f98f7cc5))
* upgrade parse from 3.1.0 to 3.2.0 ([#7378](https://github.com/parse-community/parse-server/issues/7378)) ([e9f54e2](https://github.com/parse-community/parse-server/commit/e9f54e2bddd8430864b1628ab077a0a3aae73e47))
* upgrade pg-promise from 10.10.1 to 10.10.2 ([#7399](https://github.com/parse-community/parse-server/issues/7399)) ([d365f1f](https://github.com/parse-community/parse-server/commit/d365f1f7cb44cfdeac9931308ff25dcd9ba3d79b))
* upgrade pg-promise from 10.10.2 to 10.11.0 ([#7510](https://github.com/parse-community/parse-server/issues/7510)) ([a967e79](https://github.com/parse-community/parse-server/commit/a967e7921961ea7a33ae9ff02555839cecf2b15f))
* upgrade pg-promise from 10.8.1 to 10.8.6 ([#7118](https://github.com/parse-community/parse-server/issues/7118)) ([8851810](https://github.com/parse-community/parse-server/commit/8851810a85747456cd2f3e5d3876371306b8c23d))
* upgrade pg-promise from 10.8.6 to 10.8.7 ([#7148](https://github.com/parse-community/parse-server/issues/7148)) ([231c669](https://github.com/parse-community/parse-server/commit/231c669133eea494f2db7f23fdff2e9d004c9e18))
* upgrade pg-promise from 10.8.7 to 10.9.0 ([#7168](https://github.com/parse-community/parse-server/issues/7168)) ([fcacd4d](https://github.com/parse-community/parse-server/commit/fcacd4d24e34ff48367d2c68bb97743b8a20216c))
* upgrade pg-promise from 10.9.0 to 10.9.1 ([#7170](https://github.com/parse-community/parse-server/issues/7170)) ([cca493b](https://github.com/parse-community/parse-server/commit/cca493b9fb33a4c34eaaf624ec4aedd18b11dca7))
* upgrade pg-promise from 10.9.1 to 10.9.2 ([#7209](https://github.com/parse-community/parse-server/issues/7209)) ([c05102b](https://github.com/parse-community/parse-server/commit/c05102b90c925498459b45c755298cb482ac596c))
* upgrade redis from 3.1.1 to 3.1.2 ([#7387](https://github.com/parse-community/parse-server/issues/7387)) ([f65bd22](https://github.com/parse-community/parse-server/commit/f65bd228fb3491ba217558ec288dd2ee60d0d37d))
* upgrade semver from 7.3.2 to 7.3.4 ([#7092](https://github.com/parse-community/parse-server/issues/7092)) ([7e687b1](https://github.com/parse-community/parse-server/commit/7e687b1e943372d105ca359177824241af278534))
* upgrade subscriptions-transport-ws from 0.9.19 to 0.10.0 ([#7450](https://github.com/parse-community/parse-server/issues/7450)) ([d36a53b](https://github.com/parse-community/parse-server/commit/d36a53b2bfade99942e3c9b827c9abb38d2de26b))
* upgrade uuid from 8.3.1 to 8.3.2 ([#7101](https://github.com/parse-community/parse-server/issues/7101)) ([f17a063](https://github.com/parse-community/parse-server/commit/f17a063209b451da291918dac165e177f0bee6a0))
* upgrade winston-daily-rotate-file from 4.5.0 to 4.5.1 ([#7309](https://github.com/parse-community/parse-server/issues/7309)) ([8643ae4](https://github.com/parse-community/parse-server/commit/8643ae438f1ea48c93d2860889568f9f3a5a62b8))
* upgrade winston-daily-rotate-file from 4.5.1 to 4.5.2 ([#7376](https://github.com/parse-community/parse-server/issues/7376)) ([e143fb1](https://github.com/parse-community/parse-server/commit/e143fb1bf6a52bb21c483fbf72f74d2282cfbeee))
* upgrade winston-daily-rotate-file from 4.5.2 to 4.5.3 ([#7398](https://github.com/parse-community/parse-server/issues/7398)) ([e9d8ed4](https://github.com/parse-community/parse-server/commit/e9d8ed4acb03160c8f6d669859fe57d8281937b4))
* upgrade winston-daily-rotate-file from 4.5.3 to 4.5.4 ([#7402](https://github.com/parse-community/parse-server/issues/7402)) ([4f80a5f](https://github.com/parse-community/parse-server/commit/4f80a5f4afbc008d2f642ee0c581a25483240e9a))
* upgrade winston-daily-rotate-file from 4.5.4 to 4.5.5 ([#7407](https://github.com/parse-community/parse-server/issues/7407)) ([5abbeeb](https://github.com/parse-community/parse-server/commit/5abbeeb8d11be810993c528b8458be894039a5fc))
* upgrade ws from 7.4.0 to 7.4.1 ([#7098](https://github.com/parse-community/parse-server/issues/7098)) ([1068838](https://github.com/parse-community/parse-server/commit/106883809c96a758bb23ae6dd7fff7465fbd8f60))
* upgrade ws from 7.4.1 to 7.4.2 ([#7132](https://github.com/parse-community/parse-server/issues/7132)) ([857d4ec](https://github.com/parse-community/parse-server/commit/857d4ecfd5a383d6debd849a1af1d45820aee0c0))
* upgrade ws from 7.4.2 to 7.4.3 ([#7224](https://github.com/parse-community/parse-server/issues/7224)) ([ec8f784](https://github.com/parse-community/parse-server/commit/ec8f78424f0043fa74ef1f3a7baa96a3d1e95b59))
* upgrade ws from 7.4.3 to 7.4.4 ([#7298](https://github.com/parse-community/parse-server/issues/7298)) ([a080e4c](https://github.com/parse-community/parse-server/commit/a080e4c766c33cc5b5c304bbdba69e93743f9db1))
* upgrade ws from 7.4.4 to 7.4.5 ([#7381](https://github.com/parse-community/parse-server/issues/7381)) ([34f3dd9](https://github.com/parse-community/parse-server/commit/34f3dd9e7e1908b713355ddb235b1e5d02634663))
* upgrade ws from 7.5.3 to 8.2.1 ([#7580](https://github.com/parse-community/parse-server/issues/7580)) ([c3da290](https://github.com/parse-community/parse-server/commit/c3da2908fabfa4f7f49c0f6d5b3d32de692dd388))
* upgrade ws from 8.2.1 to 8.2.2 ([#7598](https://github.com/parse-community/parse-server/issues/7598)) ([20cb333](https://github.com/parse-community/parse-server/commit/20cb3333aba67fb89e4e2cda586c559bdb4a23ef))

### Features

* add support for Postgres 14 ([#7644](https://github.com/parse-community/parse-server/issues/7644)) ([090350a](https://github.com/parse-community/parse-server/commit/090350a7a0fac945394ca1cb24b290316ef06aa7))
* add user-defined schema and migrations ([#7418](https://github.com/parse-community/parse-server/issues/7418)) ([25d5c30](https://github.com/parse-community/parse-server/commit/25d5c30be2111be332eb779eb0697774a17da7af))
* alphabetical graphql api, fix internal reassign, enhanced Graphql schema cache system ([#7344](https://github.com/parse-community/parse-server/issues/7344)) ([85ef721](https://github.com/parse-community/parse-server/commit/85ef7217b04e36395e46316004293e9c8ca67df9))
* bump required node engine to >=12.22.10 ([#7848](https://github.com/parse-community/parse-server/issues/7848)) ([23a3488](https://github.com/parse-community/parse-server/commit/23a3488f15511fafbe0e1d7ff0ef8355f9cb0215))
* **LiveQuery:** Support $and, $nor, $containedBy, $geoWithin ([#7113](https://github.com/parse-community/parse-server/issues/7113)) ([93781b2](https://github.com/parse-community/parse-server/commit/93781b2195d8074032d2c4dfd77a56acf17dc9e1))
* **AggregateRouter:** support native mongodb syntax in aggregation pipelines ([#7339](https://github.com/parse-community/parse-server/issues/7339)) ([8fddac3](https://github.com/parse-community/parse-server/commit/8fddac39bf866886c3b432219acc4d5e8169ddc0))

### Reverts

* refactor: allow ES import for cloud string if package type is module ([#7691](https://github.com/parse-community/parse-server/issues/7691)) ([200d4ba](https://github.com/parse-community/parse-server/commit/200d4ba9a527016a65668738c7728696f443bd53))

### BREAKING CHANGES

* This requires Node.js version >=12.22.10. ([23a3488](23a3488))
* To delete a field via the GraphQL API, the field value has to be set to `null`. Previously, setting a field value to `null` would save a null value in the database, which was not according to the [GraphQL specs](https://spec.graphql.org/June2018/#sec-Null-Value). To delete a file field use `file: null`, the previous way of using `file: { file: null }` has become obsolete. ([626fad2](626fad2))
2022-03-14 12:43:23 +00:00
Manuel 33dcf6dd7b build: release 5.0 2022-03-14 13:42:06 +01:00
Manuel 1f6e19fcce docs: add major release instructions to CONTRIBUTING guide (#7854) 2022-03-13 13:44:24 +01:00
Manuel b2a2a7e1f5 Merge branch 'release' into build-release 2022-03-12 18:45:53 +01:00
Manuel 50072bdd38 ci: add branch name change (#7853) 2022-03-12 18:45:07 +01:00
Manuel 2dceec7359 docs: improve explanation for commit type usage in CONTRIBUTING guide (#7849) 2022-03-12 16:23:25 +01:00
semantic-release-bot f5ef2e9162 chore(release): 5.0.0-beta.9 [skip ci]
# [5.0.0-beta.9](https://github.com/parse-community/parse-server/compare/5.0.0-beta.8...5.0.0-beta.9) (2022-03-12)

### Features

* bump required node engine to >=12.22.10 ([#7848](https://github.com/parse-community/parse-server/issues/7848)) ([23a3488](https://github.com/parse-community/parse-server/commit/23a3488f15511fafbe0e1d7ff0ef8355f9cb0215))

### BREAKING CHANGES

* This requires Node.js version >=12.22.10. ([23a3488](23a3488))
2022-03-12 15:17:00 +00:00
Manuel 23a3488f15 feat: bump required node engine to >=12.22.10 (#7848)
BREAKING CHANGE: This requires Node.js version >=12.22.10.
2022-03-12 16:15:45 +01:00
semantic-release-bot 3e68491df0 chore(release): 5.0.0-alpha.29 [skip ci]
# [5.0.0-alpha.29](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.28...5.0.0-alpha.29) (2022-03-12)

### Features

* bump required node engine to >=12.22.10 ([#7846](https://github.com/parse-community/parse-server/issues/7846)) ([5ace99d](https://github.com/parse-community/parse-server/commit/5ace99d542a11e422af46d9fd6b1d3d2513b34cf))

### BREAKING CHANGES

* This requires Node.js version >=12.22.10. ([5ace99d](5ace99d))
2022-03-12 14:12:38 +00:00
Manuel 5ace99d542 feat: bump required node engine to >=12.22.10 (#7846)
BREAKING CHANGE: This requires Node.js version >=12.22.10.
2022-03-12 15:10:44 +01:00
semantic-release-bot 9b344da9d0 chore(release): 5.0.0-alpha.28 [skip ci]
# [5.0.0-alpha.28](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.27...5.0.0-alpha.28) (2022-03-12)

### Bug Fixes

* security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) ([#7844](https://github.com/parse-community/parse-server/issues/7844)) ([e569f40](https://github.com/parse-community/parse-server/commit/e569f402b1fd8648fb0d1523b71b2a03273902a5))
2022-03-12 13:48:37 +00:00
Manuel e569f402b1 fix: security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) (#7844) 2022-03-12 14:47:23 +01:00
Manuel 972b800ae4 docs: add revert commit instructions to CONTRIBUTING guide (#7845) 2022-03-12 14:30:59 +01:00
semantic-release-bot 6bc021ee14 chore(release): 5.0.0-alpha.27 [skip ci]
# [5.0.0-alpha.27](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.26...5.0.0-alpha.27) (2022-03-12)

### Reverts

* update node engine to 2.22.0 ([#7827](https://github.com/parse-community/parse-server/issues/7827)) ([f235412](https://github.com/parse-community/parse-server/commit/f235412c1b6c2b173b7531f285429ea7214b56a2))
2022-03-12 13:14:36 +00:00
Manuel f235412c1b revert: update node engine to 2.22.0 (#7827)
This reverts commit 8a126fc1a2.
2022-03-12 14:13:38 +01:00
semantic-release-bot d35cd47c08 chore(release): 5.0.0-beta.8 [skip ci]
# [5.0.0-beta.8](https://github.com/parse-community/parse-server/compare/5.0.0-beta.7...5.0.0-beta.8) (2022-03-12)

### Bug Fixes

* security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) ([#7843](https://github.com/parse-community/parse-server/issues/7843)) ([971adb5](https://github.com/parse-community/parse-server/commit/971adb54387b0ede31be05ca407d5f35b4575c83))
2022-03-12 12:50:48 +00:00
Manuel 971adb5438 fix: security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) (#7843) 2022-03-12 13:49:57 +01:00
Antonio Davi Macedo Coelho de Castro 8a126fc1a2 Update node engine to 2.22.0 (#7827) 2022-02-25 15:01:15 -08:00
semantic-release-bot ad5d17953b chore(release): 5.0.0-alpha.26 [skip ci]
# [5.0.0-alpha.26](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.25...5.0.0-alpha.26) (2022-02-25)

### Bug Fixes

* package.json & package-lock.json to reduce vulnerabilities ([#7823](https://github.com/parse-community/parse-server/issues/7823)) ([5ca2288](https://github.com/parse-community/parse-server/commit/5ca228882332b65f3ac05407e6e4da1ee3ef3749))
2022-02-25 16:57:59 +00:00
Snyk bot 5ca2288823 fix: package.json & package-lock.json to reduce vulnerabilities (#7823)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-NODEFETCH-2342118
2022-02-25 08:56:29 -08:00
semantic-release-bot 9806912045 chore(release): 5.0.0-alpha.25 [skip ci]
# [5.0.0-alpha.25](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.24...5.0.0-alpha.25) (2022-02-23)

### Bug Fixes

* upgrade winston from 3.5.0 to 3.5.1 ([#7820](https://github.com/parse-community/parse-server/issues/7820)) ([4af253d](https://github.com/parse-community/parse-server/commit/4af253d1f8654a6f57b5137ad310cdacadc922cc))
2022-02-23 00:56:33 +00:00
Snyk bot 4af253d1f8 fix: upgrade winston from 3.5.0 to 3.5.1 (#7820) 2022-02-23 01:55:33 +01:00
Snyk bot 2a6cec173e refactor: upgrade winston from 3.4.0 to 3.5.0 (#7817) 2022-02-20 18:26:13 +01:00
Snyk bot c7012012aa refactor: upgrade winston-daily-rotate-file from 4.5.5 to 4.6.0 (#7816) 2022-02-18 21:44:52 +01:00
Snyk bot 8cece4a2a8 refactor: upgrade @apollo/client from 3.5.7 to 3.5.8 (#7814) 2022-02-16 23:48:05 +01:00
Snyk bot 7a30d7c885 refactor: upgrade lru-cache from 5.1.1 to 6.0.0 (#7813) 2022-02-15 12:42:13 +01:00
Snyk bot b6a20d9e9f refactor: upgrade winston from 3.3.3 to 3.4.0 (#7812) 2022-02-14 10:13:35 +01:00
Snyk bot 82c3015dc0 refactor: upgrade @parse/push-adapter from 3.4.1 to 4.1.0 (#7810) 2022-02-13 11:14:57 +01:00
semantic-release-bot a48015c3b0 chore(release): 5.0.0-beta.7 [skip ci]
# [5.0.0-beta.7](https://github.com/parse-community/parse-server/compare/5.0.0-beta.6...5.0.0-beta.7) (2022-02-10)

### Bug Fixes

* security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7802](https://github.com/parse-community/parse-server/issues/7802)) ([7029b27](https://github.com/parse-community/parse-server/commit/7029b274ca87bc8058617f29865d683dc3b351a1))
2022-02-10 11:12:15 +00:00
semantic-release-bot d678a43696 chore(release): 5.0.0-alpha.24 [skip ci]
# [5.0.0-alpha.24](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.23...5.0.0-alpha.24) (2022-02-10)

### Bug Fixes

* security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7801](https://github.com/parse-community/parse-server/issues/7801)) ([70088a9](https://github.com/parse-community/parse-server/commit/70088a95a78393da2a4ac68be81e63107747626a))
2022-02-10 11:11:17 +00:00
Snyk bot 7029b274ca fix: security upgrade follow-redirects from 1.14.7 to 1.14.8 (#7802) 2022-02-10 12:11:08 +01:00
Snyk bot 70088a95a7 fix: security upgrade follow-redirects from 1.14.7 to 1.14.8 (#7801) 2022-02-10 12:10:14 +01:00
Snyk bot fc9d77f1d4 refactor: upgrade jwks-rsa from 1.12.3 to 2.0.5 (#7800) 2022-02-09 19:07:53 +01:00
Snyk bot c6b2032039 refactor: upgrade @apollo/client from 3.4.17 to 3.5.7 (#7797) 2022-02-08 11:02:45 +01:00
semantic-release-bot 43cc97d99f chore(release): 5.0.0-alpha.23 [skip ci]
# [5.0.0-alpha.23](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.22...5.0.0-alpha.23) (2022-02-06)

### Bug Fixes

* server crash using GraphQL due to missing @apollo/client peer dependency ([#7787](https://github.com/parse-community/parse-server/issues/7787)) ([08089d6](https://github.com/parse-community/parse-server/commit/08089d6fcbb215412448ce7d92b21b9fe6c929f2))
2022-02-06 23:10:52 +00:00
Antoine Cormouls 08089d6fcb fix: server crash using GraphQL due to missing @apollo/client peer dependency (#7787) 2022-02-07 00:09:41 +01:00
semantic-release-bot 1e05309e79 chore(release): 5.0.0-alpha.22 [skip ci]
# [5.0.0-alpha.22](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.21...5.0.0-alpha.22) (2022-02-06)

### Features

* upgrade to MongoDB Node.js driver 4.x for MongoDB 5.0 support ([#7794](https://github.com/parse-community/parse-server/issues/7794)) ([f88aa2a](https://github.com/parse-community/parse-server/commit/f88aa2a62a533e5344d1c13dd38c5a0b283a480a))

### BREAKING CHANGES

* The MongoDB GridStore adapter has been removed. By default, Parse Server already uses GridFS, so if you do not manually use the GridStore adapter, you can ignore this change. ([f88aa2a](f88aa2a))
2022-02-06 17:31:41 +00:00
Antoine Cormouls f88aa2a62a feat: upgrade to MongoDB Node.js driver 4.x for MongoDB 5.0 support (#7794)
BREAKING CHANGE: The MongoDB GridStore adapter has been removed. By default, Parse Server already uses GridFS, so if you do not manually use the GridStore adapter, you can ignore this change.
2022-02-06 18:30:36 +01:00
semantic-release-bot 1299f0697c chore(release): 5.0.0-alpha.21 [skip ci]
# [5.0.0-alpha.21](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.20...5.0.0-alpha.21) (2022-01-25)

### Features

* add Cloud Code context to `ParseObject.fetch` ([#7779](https://github.com/parse-community/parse-server/issues/7779)) ([315290d](https://github.com/parse-community/parse-server/commit/315290d16110110938f80a6b779cc2d1db58c552))
2022-01-25 11:41:15 +00:00
yog27ray 315290d161 feat: add Cloud Code context to ParseObject.fetch (#7779) 2022-01-25 12:40:22 +01:00
Snyk bot 433e82f1df refactor: upgrade parse from 3.4.0 to 3.4.1 (#7783) 2022-01-25 01:42:55 +01:00
semantic-release-bot be37266595 chore(release): 5.0.0-beta.6 [skip ci]
# [5.0.0-beta.6](https://github.com/parse-community/parse-server/compare/5.0.0-beta.5...5.0.0-beta.6) (2022-01-13)

### Bug Fixes

* security upgrade follow-redirects from 1.14.2 to 1.14.7 ([#7772](https://github.com/parse-community/parse-server/issues/7772)) ([4bd34b1](https://github.com/parse-community/parse-server/commit/4bd34b189bc9f5aa2e70b7e7c1a456e91b6de773))
2022-01-13 11:43:30 +00:00
Snyk bot 4bd34b189b fix: security upgrade follow-redirects from 1.14.2 to 1.14.7 (#7772) 2022-01-13 12:42:25 +01:00
semantic-release-bot f90461e693 chore(release): 5.0.0-beta.5 [skip ci]
# [5.0.0-beta.5](https://github.com/parse-community/parse-server/compare/5.0.0-beta.4...5.0.0-beta.5) (2022-01-13)

### Bug Fixes

* schema cache not cleared in some cases ([#7771](https://github.com/parse-community/parse-server/issues/7771)) ([3b92fa1](https://github.com/parse-community/parse-server/commit/3b92fa1ca9e8889127a32eba913d68309397ca2c))
2022-01-13 02:06:03 +00:00
Manuel 3b92fa1ca9 fix: schema cache not cleared in some cases (#7771) 2022-01-13 03:04:49 +01:00
semantic-release-bot 66347dc44a chore(release): 5.0.0-beta.4 [skip ci]
# [5.0.0-beta.4](https://github.com/parse-community/parse-server/compare/5.0.0-beta.3...5.0.0-beta.4) (2021-11-27)

### Bug Fixes

* unable to use objectId size higher than 19 on GraphQL API ([#7722](https://github.com/parse-community/parse-server/issues/7722)) ([8ee0445](https://github.com/parse-community/parse-server/commit/8ee0445c0aeeb88dff2559b46ade408071d22143))
2021-11-27 12:37:38 +00:00
Manuel 8ee0445c0a fix: unable to use objectId size higher than 19 on GraphQL API (#7722) 2021-11-27 13:36:49 +01:00
semantic-release-bot 2923833b25 chore(release): 5.0.0-beta.3 [skip ci]
# [5.0.0-beta.3](https://github.com/parse-community/parse-server/compare/5.0.0-beta.2...5.0.0-beta.3) (2021-11-12)

### Bug Fixes

* node engine range has no upper limit to exclude incompatible node versions ([#7693](https://github.com/parse-community/parse-server/issues/7693)) ([6a54dac](https://github.com/parse-community/parse-server/commit/6a54dac24d9fb63a44f311b8d414f4aa64140f32))
2021-11-12 18:11:20 +00:00
Manuel 6a54dac24d fix: node engine range has no upper limit to exclude incompatible node versions (#7693) 2021-11-12 19:10:17 +01:00
semantic-release-bot 32b7194139 chore(release): 5.0.0-beta.2 [skip ci]
# [5.0.0-beta.2](https://github.com/parse-community/parse-server/compare/5.0.0-beta.1...5.0.0-beta.2) (2021-11-10)

### Reverts

* refactor: allow ES import for cloud string if package type is module ([#7691](https://github.com/parse-community/parse-server/issues/7691)) ([200d4ba](https://github.com/parse-community/parse-server/commit/200d4ba9a527016a65668738c7728696f443bd53))
2021-11-10 15:50:43 +00:00
Manuel 200d4ba9a5 revert: refactor: allow ES import for cloud string if package type is module (#7691)
This reverts commit 0225340ccb.
2021-11-10 16:49:47 +01:00
Manuel Trezza 6962bfac65 docs: enable npm beta badge on README 2021-11-01 17:25:20 +01:00
Manuel Trezza e91e388366 docs: fix changelog which was expectedly incorrect on first beta release 2021-11-01 17:23:07 +01:00
semantic-release-bot 4f114068a1 chore(release): 5.0.0-beta.1 [skip ci]
# [5.0.0-beta.1](https://github.com/parse-community/parse-server/compare/4.5.0...5.0.0-beta.1) (2021-11-01)

### Bug Fixes

* add deprecation warning for `Parse.Cloud.httpRequest` ([#7595](https://github.com/parse-community/parse-server/issues/7595)) ([ab1dddd](https://github.com/parse-community/parse-server/commit/ab1dddd406589800e1a8ebe8381b9da702e9c641))
* add support for descending sorting of full text search ([#7496](https://github.com/parse-community/parse-server/issues/7496)) ([8ed9442](https://github.com/parse-community/parse-server/commit/8ed94421e69b1d4fde4ec3625a1f4cfbd6d39c2b))
* allow LiveQuery on Parse.Session ([#7554](https://github.com/parse-community/parse-server/issues/7554)) ([caee281](https://github.com/parse-community/parse-server/commit/caee281bc58901fea7cead21d6a9a0bb2022d91b))
* combined `and` query with relational query condition returns incorrect results ([#7593](https://github.com/parse-community/parse-server/issues/7593)) ([174886e](https://github.com/parse-community/parse-server/commit/174886e385e091c6bbd4a84891ef95f80b50d05c))
* empty file tags cause upload error for some providers ([#7300](https://github.com/parse-community/parse-server/issues/7300)) ([4d16702](https://github.com/parse-community/parse-server/commit/4d167026aebed38bf3a3b74599ab3771d1da3245))
* **Logger:** Handle interpolating stdout ([#7114](https://github.com/parse-community/parse-server/issues/7114)) ([1ede078](https://github.com/parse-community/parse-server/commit/1ede078154213d3426cc492612ce41bdb1eda43b))
* improve security by deprecating creating users with public access by default ([#7319](https://github.com/parse-community/parse-server/issues/7319)) ([484c2e8](https://github.com/parse-community/parse-server/commit/484c2e81cace608eb28508a0926b17b3ba9e6233))
* package.json & package-lock.json to reduce vulnerabilities ([#7112](https://github.com/parse-community/parse-server/issues/7112)) ([7b8d8dd](https://github.com/parse-community/parse-server/commit/7b8d8ddbb0de583621a4d2ede95793c6516bc4c0))
* package.json & package-lock.json to reduce vulnerabilities ([#7218](https://github.com/parse-community/parse-server/issues/7218)) ([0476832](https://github.com/parse-community/parse-server/commit/047683219daf7940590db1237f6826a038bdc41d))
* package.json & package-lock.json to reduce vulnerabilities ([#7373](https://github.com/parse-community/parse-server/issues/7373)) ([b6843de](https://github.com/parse-community/parse-server/commit/b6843de904bf273bf664d94536defa39f91751b3))
* package.json & package-lock.json to reduce vulnerabilities ([#7405](https://github.com/parse-community/parse-server/issues/7405)) ([d915bac](https://github.com/parse-community/parse-server/commit/d915bacee7c6ded60b303582d2a6b85d42dc3cf4))
* package.json & package-lock.json to reduce vulnerabilities ([#7423](https://github.com/parse-community/parse-server/issues/7423)) ([bea4707](https://github.com/parse-community/parse-server/commit/bea4707783febc0fc6ea055835ffbc517cd2b067))
* package.json & package-lock.json to reduce vulnerabilities ([#7509](https://github.com/parse-community/parse-server/issues/7509)) ([65c967a](https://github.com/parse-community/parse-server/commit/65c967a4c446aebb535cbbbcb0ceb38d45da7859))
* Pass customObjectId in beforeSave ([#7167](https://github.com/parse-community/parse-server/issues/7167)) ([7224cde](https://github.com/parse-community/parse-server/commit/7224cde0232d79fea0f4b2d0bc2cb7641ec467d8)), closes [#6733](https://github.com/parse-community/parse-server/issues/6733)
* **utils:** permutation helper ([#7355](https://github.com/parse-community/parse-server/issues/7355)) ([91be6bb](https://github.com/parse-community/parse-server/commit/91be6bb59a61c126b7a25431655064342464905c))
* set objects in afterFind triggers ([#7311](https://github.com/parse-community/parse-server/issues/7311)) ([68a3a87](https://github.com/parse-community/parse-server/commit/68a3a875017d545253f0438c5d4c1c2b6c6c3b22))
* setting a field to null does not delete it via GraphQL API ([#7649](https://github.com/parse-community/parse-server/issues/7649)) ([626fad2](https://github.com/parse-community/parse-server/commit/626fad2e71017dcc62196c487de5f908fa43000b))
* upgrade @apollographql/graphql-playground-html from 1.6.26 to 1.6.27 ([#7274](https://github.com/parse-community/parse-server/issues/7274)) ([a05e9b1](https://github.com/parse-community/parse-server/commit/a05e9b1c0fee8ad917812637256faa1e0a1b40f6))
* upgrade @apollographql/graphql-playground-html from 1.6.27 to 1.6.28 ([#7411](https://github.com/parse-community/parse-server/issues/7411)) ([c58bf57](https://github.com/parse-community/parse-server/commit/c58bf57f51ebc9df3a624fb3ecdacdf4647f1dea))
* upgrade @apollographql/graphql-playground-html from 1.6.28 to 1.6.29 ([#7473](https://github.com/parse-community/parse-server/issues/7473)) ([39f7c83](https://github.com/parse-community/parse-server/commit/39f7c831e6493cff9cd64195b64e886fda4624f8))
* upgrade @parse/simple-mailgun-adapter from 1.1.0 to 1.2.0 ([#7109](https://github.com/parse-community/parse-server/issues/7109)) ([8ff0d08](https://github.com/parse-community/parse-server/commit/8ff0d08dcf5a9ada931359df3e78543445312f15))
* upgrade apollo-server-express from 2.19.0 to 2.19.1 ([#7122](https://github.com/parse-community/parse-server/issues/7122)) ([33bdd87](https://github.com/parse-community/parse-server/commit/33bdd8709266f17489c319e43b3f2a7d8ea27fd5))
* upgrade apollo-server-express from 2.19.1 to 2.19.2 ([#7165](https://github.com/parse-community/parse-server/issues/7165)) ([4b6e9ff](https://github.com/parse-community/parse-server/commit/4b6e9ffc3d2c1969c285c94f7cf2cfc7af03227e))
* upgrade apollo-server-express from 2.19.2 to 2.20.0 ([#7239](https://github.com/parse-community/parse-server/issues/7239)) ([d10e990](https://github.com/parse-community/parse-server/commit/d10e99007b70816707944060905cff7da5018eb0))
* upgrade apollo-server-express from 2.21.0 to 2.21.1 ([#7308](https://github.com/parse-community/parse-server/issues/7308)) ([3dc4597](https://github.com/parse-community/parse-server/commit/3dc459746dbd4a2d245f2bde688cd087a5b74703))
* upgrade apollo-server-express from 2.21.1 to 2.22.1 ([#7357](https://github.com/parse-community/parse-server/issues/7357)) ([25690ad](https://github.com/parse-community/parse-server/commit/25690ad515551bda33dd581789ffeb9a218c8033))
* upgrade apollo-server-express from 2.22.1 to 2.22.2 ([#7362](https://github.com/parse-community/parse-server/issues/7362)) ([181fbf9](https://github.com/parse-community/parse-server/commit/181fbf9d46bee61b5c67a411b4a41c38d201cc73))
* upgrade apollo-server-express from 2.22.2 to 2.23.0 ([#7380](https://github.com/parse-community/parse-server/issues/7380)) ([87476da](https://github.com/parse-community/parse-server/commit/87476da31e28e064fa7192998c67de9c31ac18a6))
* upgrade apollo-server-express from 2.23.0 to 2.24.0 ([#7395](https://github.com/parse-community/parse-server/issues/7395)) ([ff5755b](https://github.com/parse-community/parse-server/commit/ff5755b05ac554a107a443a8870f4c2442b0a5f3))
* upgrade apollo-server-express from 2.24.0 to 2.24.1 ([#7424](https://github.com/parse-community/parse-server/issues/7424)) ([bfdb6a9](https://github.com/parse-community/parse-server/commit/bfdb6a93e8c0d78524820b32abaff174776520f3))
* upgrade apollo-server-express from 2.24.1 to 2.25.0 ([#7435](https://github.com/parse-community/parse-server/issues/7435)) ([4e5eba6](https://github.com/parse-community/parse-server/commit/4e5eba6c6c7bc6e643ac6477df15beea1ba60364))
* upgrade apollo-server-express from 2.25.0 to 2.25.1 ([#7449](https://github.com/parse-community/parse-server/issues/7449)) ([682f1bf](https://github.com/parse-community/parse-server/commit/682f1bf14388e134fbd993e274f5738ec2e57e02))
* upgrade apollo-server-express from 2.25.1 to 2.25.2 ([#7465](https://github.com/parse-community/parse-server/issues/7465)) ([1fe4708](https://github.com/parse-community/parse-server/commit/1fe47087a92c80e7ee8cc6e3e26b3a67f8df68ee))
* upgrade follow-redirects from 1.13.0 to 1.13.1 ([#7106](https://github.com/parse-community/parse-server/issues/7106)) ([16b4aad](https://github.com/parse-community/parse-server/commit/16b4aadfe2b5b5b24e6e3bbe22c93a92f02c9eb3))
* upgrade follow-redirects from 1.13.1 to 1.13.2 ([#7194](https://github.com/parse-community/parse-server/issues/7194)) ([738ba9f](https://github.com/parse-community/parse-server/commit/738ba9fc704a770bd1e2af23bca31ed7202f99cd))
* upgrade follow-redirects from 1.13.2 to 1.13.3 ([#7285](https://github.com/parse-community/parse-server/issues/7285)) ([d144819](https://github.com/parse-community/parse-server/commit/d144819d21b59b11dc957346748ce3200349a6b7))
* upgrade follow-redirects from 1.13.3 to 1.14.0 ([#7389](https://github.com/parse-community/parse-server/issues/7389)) ([38c01c6](https://github.com/parse-community/parse-server/commit/38c01c6bc32fe769c3a636e45b389e2a995c2ee6))
* upgrade follow-redirects from 1.14.0 to 1.14.1 ([#7408](https://github.com/parse-community/parse-server/issues/7408)) ([8976ecc](https://github.com/parse-community/parse-server/commit/8976ecc4a9c91eeb1d0f3448eff7720b43c7747a))
* upgrade graphql from 15.4.0 to 15.5.0 ([#7201](https://github.com/parse-community/parse-server/issues/7201)) ([5a09687](https://github.com/parse-community/parse-server/commit/5a0968721d6d04cdde86d3df569a77910bcae803))
* upgrade graphql from 15.5.0 to 15.5.1 ([#7462](https://github.com/parse-community/parse-server/issues/7462)) ([bbd7ee7](https://github.com/parse-community/parse-server/commit/bbd7ee7313e19c43b15e16027439739590393dd1))
* upgrade graphql from 15.5.1 to 15.5.2 ([#7587](https://github.com/parse-community/parse-server/issues/7587)) ([dee4d96](https://github.com/parse-community/parse-server/commit/dee4d9662749eb699891c8a3797fa0b0467c6355))
* upgrade graphql from 15.5.2 to 15.5.3 ([#7596](https://github.com/parse-community/parse-server/issues/7596)) ([bcbc035](https://github.com/parse-community/parse-server/commit/bcbc035627c552a7940bec4ae210ca1522215246))
* upgrade graphql from 15.5.3 to 15.6.0 ([#7612](https://github.com/parse-community/parse-server/issues/7612)) ([407ed6e](https://github.com/parse-community/parse-server/commit/407ed6ee36ea42d7eb525f42b1f3da6f41230504))
* upgrade graphql-relay from 0.6.0 to 0.7.0 ([#7443](https://github.com/parse-community/parse-server/issues/7443)) ([770e36f](https://github.com/parse-community/parse-server/commit/770e36ff43c42e4ed3db73a30436079a0460a1f0))
* upgrade graphql-relay from 0.7.0 to 0.8.0 ([#7467](https://github.com/parse-community/parse-server/issues/7467)) ([9923cd3](https://github.com/parse-community/parse-server/commit/9923cd3869ab80e70c5ca9765628ba0a28a6facc))
* upgrade graphql-tag from 2.10.1 to 2.12.0 ([#7234](https://github.com/parse-community/parse-server/issues/7234)) ([add67fd](https://github.com/parse-community/parse-server/commit/add67fdd2284a58386f6afd35edeaa8534579e33))
* upgrade graphql-tag from 2.12.0 to 2.12.1 ([#7282](https://github.com/parse-community/parse-server/issues/7282)) ([36de1db](https://github.com/parse-community/parse-server/commit/36de1db65dc3c2bbf8f8051ee1862a06ef41e7f9))
* upgrade graphql-tag from 2.12.1 to 2.12.2 ([#7325](https://github.com/parse-community/parse-server/issues/7325)) ([50e5557](https://github.com/parse-community/parse-server/commit/50e55571fdaa1ac3ca43d215c4bc62136d684873))
* upgrade graphql-tag from 2.12.2 to 2.12.4 ([#7396](https://github.com/parse-community/parse-server/issues/7396)) ([8099cb0](https://github.com/parse-community/parse-server/commit/8099cb05a48956b073f3e7d96c30b69292c06adc))
* upgrade graphql-tag from 2.12.4 to 2.12.5 ([#7466](https://github.com/parse-community/parse-server/issues/7466)) ([2b3355c](https://github.com/parse-community/parse-server/commit/2b3355cb022724838499a5d83c0d532040953566))
* upgrade jwks-rsa from 1.11.0 to 1.12.0 ([#7102](https://github.com/parse-community/parse-server/issues/7102)) ([029edbf](https://github.com/parse-community/parse-server/commit/029edbf706c4b1754afbb5ce69600a373b91cb9b))
* upgrade jwks-rsa from 1.12.1 to 1.12.2 ([#7147](https://github.com/parse-community/parse-server/issues/7147)) ([bcb2b52](https://github.com/parse-community/parse-server/commit/bcb2b52f7a1322a780b95b77081827d6c8c25840))
* upgrade jwks-rsa from 1.12.2 to 1.12.3 ([#7284](https://github.com/parse-community/parse-server/issues/7284)) ([a53d74c](https://github.com/parse-community/parse-server/commit/a53d74c13b86d43e1694132feddccce6ee2bede9))
* upgrade ldapjs from 2.2.2 to 2.2.3 ([#7095](https://github.com/parse-community/parse-server/issues/7095)) ([fb465e5](https://github.com/parse-community/parse-server/commit/fb465e599e8768b9e229ec8689e033d7063e50b3))
* upgrade ldapjs from 2.2.3 to 2.2.4 ([#7275](https://github.com/parse-community/parse-server/issues/7275)) ([35f0c55](https://github.com/parse-community/parse-server/commit/35f0c55e93dc0572049a85befbc80175620f7d29))
* upgrade ldapjs from 2.2.4 to 2.3.0 ([#7436](https://github.com/parse-community/parse-server/issues/7436)) ([7df6c02](https://github.com/parse-community/parse-server/commit/7df6c020b18b8ea9d04b718fa4e070be800b9268))
* upgrade ldapjs from 2.3.0 to 2.3.1 ([#7524](https://github.com/parse-community/parse-server/issues/7524)) ([dee5a13](https://github.com/parse-community/parse-server/commit/dee5a13a85336cdd9d3bcd938c4118fffe8b9670))
* upgrade mime from 2.4.6 to 2.4.7 ([#7110](https://github.com/parse-community/parse-server/issues/7110)) ([fefcabe](https://github.com/parse-community/parse-server/commit/fefcabe858f3817dca337390c2198abbf12f9bdf))
* upgrade mime from 2.4.7 to 2.5.0 ([#7166](https://github.com/parse-community/parse-server/issues/7166)) ([6097e82](https://github.com/parse-community/parse-server/commit/6097e82194772847954dc2c2b7559543cc7531cc))
* upgrade mime from 2.5.0 to 2.5.2 ([#7261](https://github.com/parse-community/parse-server/issues/7261)) ([687f4b7](https://github.com/parse-community/parse-server/commit/687f4b7cf245ce578674a4cc0e0d276b23d5780d))
* upgrade mongodb from 3.6.6 to 3.6.7 ([#7425](https://github.com/parse-community/parse-server/issues/7425)) ([61affe2](https://github.com/parse-community/parse-server/commit/61affe26293f7218e88878d027514d68975cfc8c))
* upgrade mongodb from 3.6.7 to 3.6.8 ([#7430](https://github.com/parse-community/parse-server/issues/7430)) ([c36588e](https://github.com/parse-community/parse-server/commit/c36588e3c2d16fab3f38aecc77f0e827bda38f95))
* upgrade mongodb from 3.6.8 to 3.6.9 ([#7445](https://github.com/parse-community/parse-server/issues/7445)) ([17cf1a4](https://github.com/parse-community/parse-server/commit/17cf1a46e557b502b132c415b922508e032209a9))
* upgrade mongodb from 3.6.9 to 3.6.10 ([#7474](https://github.com/parse-community/parse-server/issues/7474)) ([45d29cc](https://github.com/parse-community/parse-server/commit/45d29cc58c373c9e1d489dbd7a750f2a0f6bc89b))
* upgrade mustache from 4.1.0 to 4.2.0 ([#7358](https://github.com/parse-community/parse-server/issues/7358)) ([94b7b32](https://github.com/parse-community/parse-server/commit/94b7b320062579e788df6c9f72d7a786f98f7cc5))
* upgrade parse from 3.1.0 to 3.2.0 ([#7378](https://github.com/parse-community/parse-server/issues/7378)) ([e9f54e2](https://github.com/parse-community/parse-server/commit/e9f54e2bddd8430864b1628ab077a0a3aae73e47))
* upgrade pg-promise from 10.10.1 to 10.10.2 ([#7399](https://github.com/parse-community/parse-server/issues/7399)) ([d365f1f](https://github.com/parse-community/parse-server/commit/d365f1f7cb44cfdeac9931308ff25dcd9ba3d79b))
* upgrade pg-promise from 10.10.2 to 10.11.0 ([#7510](https://github.com/parse-community/parse-server/issues/7510)) ([a967e79](https://github.com/parse-community/parse-server/commit/a967e7921961ea7a33ae9ff02555839cecf2b15f))
* upgrade pg-promise from 10.8.1 to 10.8.6 ([#7118](https://github.com/parse-community/parse-server/issues/7118)) ([8851810](https://github.com/parse-community/parse-server/commit/8851810a85747456cd2f3e5d3876371306b8c23d))
* upgrade pg-promise from 10.8.6 to 10.8.7 ([#7148](https://github.com/parse-community/parse-server/issues/7148)) ([231c669](https://github.com/parse-community/parse-server/commit/231c669133eea494f2db7f23fdff2e9d004c9e18))
* upgrade pg-promise from 10.8.7 to 10.9.0 ([#7168](https://github.com/parse-community/parse-server/issues/7168)) ([fcacd4d](https://github.com/parse-community/parse-server/commit/fcacd4d24e34ff48367d2c68bb97743b8a20216c))
* upgrade pg-promise from 10.9.0 to 10.9.1 ([#7170](https://github.com/parse-community/parse-server/issues/7170)) ([cca493b](https://github.com/parse-community/parse-server/commit/cca493b9fb33a4c34eaaf624ec4aedd18b11dca7))
* upgrade pg-promise from 10.9.1 to 10.9.2 ([#7209](https://github.com/parse-community/parse-server/issues/7209)) ([c05102b](https://github.com/parse-community/parse-server/commit/c05102b90c925498459b45c755298cb482ac596c))
* upgrade redis from 3.1.1 to 3.1.2 ([#7387](https://github.com/parse-community/parse-server/issues/7387)) ([f65bd22](https://github.com/parse-community/parse-server/commit/f65bd228fb3491ba217558ec288dd2ee60d0d37d))
* upgrade semver from 7.3.2 to 7.3.4 ([#7092](https://github.com/parse-community/parse-server/issues/7092)) ([7e687b1](https://github.com/parse-community/parse-server/commit/7e687b1e943372d105ca359177824241af278534))
* upgrade subscriptions-transport-ws from 0.9.19 to 0.10.0 ([#7450](https://github.com/parse-community/parse-server/issues/7450)) ([d36a53b](https://github.com/parse-community/parse-server/commit/d36a53b2bfade99942e3c9b827c9abb38d2de26b))
* upgrade uuid from 8.3.1 to 8.3.2 ([#7101](https://github.com/parse-community/parse-server/issues/7101)) ([f17a063](https://github.com/parse-community/parse-server/commit/f17a063209b451da291918dac165e177f0bee6a0))
* upgrade winston-daily-rotate-file from 4.5.0 to 4.5.1 ([#7309](https://github.com/parse-community/parse-server/issues/7309)) ([8643ae4](https://github.com/parse-community/parse-server/commit/8643ae438f1ea48c93d2860889568f9f3a5a62b8))
* upgrade winston-daily-rotate-file from 4.5.1 to 4.5.2 ([#7376](https://github.com/parse-community/parse-server/issues/7376)) ([e143fb1](https://github.com/parse-community/parse-server/commit/e143fb1bf6a52bb21c483fbf72f74d2282cfbeee))
* upgrade winston-daily-rotate-file from 4.5.2 to 4.5.3 ([#7398](https://github.com/parse-community/parse-server/issues/7398)) ([e9d8ed4](https://github.com/parse-community/parse-server/commit/e9d8ed4acb03160c8f6d669859fe57d8281937b4))
* upgrade winston-daily-rotate-file from 4.5.3 to 4.5.4 ([#7402](https://github.com/parse-community/parse-server/issues/7402)) ([4f80a5f](https://github.com/parse-community/parse-server/commit/4f80a5f4afbc008d2f642ee0c581a25483240e9a))
* upgrade winston-daily-rotate-file from 4.5.4 to 4.5.5 ([#7407](https://github.com/parse-community/parse-server/issues/7407)) ([5abbeeb](https://github.com/parse-community/parse-server/commit/5abbeeb8d11be810993c528b8458be894039a5fc))
* upgrade ws from 7.4.0 to 7.4.1 ([#7098](https://github.com/parse-community/parse-server/issues/7098)) ([1068838](https://github.com/parse-community/parse-server/commit/106883809c96a758bb23ae6dd7fff7465fbd8f60))
* upgrade ws from 7.4.1 to 7.4.2 ([#7132](https://github.com/parse-community/parse-server/issues/7132)) ([857d4ec](https://github.com/parse-community/parse-server/commit/857d4ecfd5a383d6debd849a1af1d45820aee0c0))
* upgrade ws from 7.4.2 to 7.4.3 ([#7224](https://github.com/parse-community/parse-server/issues/7224)) ([ec8f784](https://github.com/parse-community/parse-server/commit/ec8f78424f0043fa74ef1f3a7baa96a3d1e95b59))
* upgrade ws from 7.4.3 to 7.4.4 ([#7298](https://github.com/parse-community/parse-server/issues/7298)) ([a080e4c](https://github.com/parse-community/parse-server/commit/a080e4c766c33cc5b5c304bbdba69e93743f9db1))
* upgrade ws from 7.4.4 to 7.4.5 ([#7381](https://github.com/parse-community/parse-server/issues/7381)) ([34f3dd9](https://github.com/parse-community/parse-server/commit/34f3dd9e7e1908b713355ddb235b1e5d02634663))
* upgrade ws from 7.5.3 to 8.2.1 ([#7580](https://github.com/parse-community/parse-server/issues/7580)) ([c3da290](https://github.com/parse-community/parse-server/commit/c3da2908fabfa4f7f49c0f6d5b3d32de692dd388))
* upgrade ws from 8.2.1 to 8.2.2 ([#7598](https://github.com/parse-community/parse-server/issues/7598)) ([20cb333](https://github.com/parse-community/parse-server/commit/20cb3333aba67fb89e4e2cda586c559bdb4a23ef))

### Features

* add support for Postgres 14 ([#7644](https://github.com/parse-community/parse-server/issues/7644)) ([090350a](https://github.com/parse-community/parse-server/commit/090350a7a0fac945394ca1cb24b290316ef06aa7))
* add user-defined schema and migrations ([#7418](https://github.com/parse-community/parse-server/issues/7418)) ([25d5c30](https://github.com/parse-community/parse-server/commit/25d5c30be2111be332eb779eb0697774a17da7af))
* alphabetical graphql api, fix internal reassign, enhanced Graphql schema cache system ([#7344](https://github.com/parse-community/parse-server/issues/7344)) ([85ef721](https://github.com/parse-community/parse-server/commit/85ef7217b04e36395e46316004293e9c8ca67df9))
* **LiveQuery:** Support $and, $nor, $containedBy, $geoWithin ([#7113](https://github.com/parse-community/parse-server/issues/7113)) ([93781b2](https://github.com/parse-community/parse-server/commit/93781b2195d8074032d2c4dfd77a56acf17dc9e1))
* **AggregateRouter:** support native mongodb syntax in aggregation pipelines ([#7339](https://github.com/parse-community/parse-server/issues/7339)) ([8fddac3](https://github.com/parse-community/parse-server/commit/8fddac39bf866886c3b432219acc4d5e8169ddc0))

### BREAKING CHANGES

* To delete a field via the GraphQL API, the field value has to be set to `null`. Previously, setting a field value to `null` would save a null value in the database, which was not according to the [GraphQL specs](https://spec.graphql.org/June2018/#sec-Null-Value). To delete a file field use `file: null`, the previous way of using `file: { file: null }` has become obsolete. ([626fad2](626fad2))
2021-11-01 16:12:16 +00:00
Manuel e94a08ffdf build: release beta 2021-11-01 17:11:15 +01:00
47 changed files with 2192 additions and 1156 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
- run: npx semantic-release
env:
GH_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Determine tag on current commit
id: tag
+10
View File
@@ -0,0 +1,10 @@
{
"detectiveOptions": {
"ts": {
"skipTypeImports": true
},
"es6": {
"skipTypeImports": true
}
}
}
+1 -2
View File
@@ -42,8 +42,7 @@ Details:
- Suitable environment: experimental
[log_release]: https://github.com/parse-community/parse-server/blob/release/CHANGELOG.md
<!--[log_release]: https://github.com/parse-community/parse-server/blob/release/changelogs/CHANGELOG_release.md -->
[log_release]: https://github.com/parse-community/parse-server/blob/release/changelogs/CHANGELOG_release.md
[log_beta]: https://github.com/parse-community/parse-server/blob/beta/changelogs/CHANGELOG_beta.md
[log_alpha]: https://github.com/parse-community/parse-server/blob/alpha/changelogs/CHANGELOG_alpha.md
[branch_release]: https://github.com/parse-community/parse-server/tree/release
+62 -5
View File
@@ -22,6 +22,11 @@
- [Pull Request](#pull-request)
- [Breaking Change](#breaking-change)
- [Merging](#merging)
- [Breaking Change](#breaking-change-1)
- [Reverting](#reverting)
- [Releasing](#releasing)
- [General Considerations](#general-considerations)
- [Major Release / Long-Term-Support](#major-release--long-term-support)
- [Versioning](#versioning)
- [Code of Conduct](#code-of-conduct)
@@ -303,7 +308,7 @@ For release automation, the title of pull requests needs to be written in a defi
```
The _type_ is the category of change that is made, possible types are:
- `feat` - add a new feature
- `feat` - add a new feature or improve an existing feature
- `fix` - fix a bug
- `refactor` - refactor code without impact on features or performance
- `docs` - add or edit code comments, documentation, GitHub pages
@@ -335,8 +340,13 @@ If a pull request contains a braking change, the description of the pull request
The following guide is for anyone who merges a contributor pull request into the working branch, the working branch into a release branch, a release branch into another release branch, or any other direct commits such as hotfixes into release branches or the working branch.
- For changelog generation, only the commit message set when merging the pull request is relevant. The title and description of the GitHub pull request as authored by the contributor have no influence on the changelog generation. However, the title of the GitHub pull request should be used as the commit message.
- If the pull request contains a breaking change, the commit message must contain the phrase `BREAKING CHANGE`, capitalized and without any formatting, followed by a short description of the breaking change and ideally how the developer should address it, all in a single line. This line should contain more details focusing on the "breaking” aspect of the change and is intended to assist the developer in adapting. Keep it concise, as it will become part of the changelog entry, for example:
- A contributor pull request must be merged into the working branch using `Squash and Merge`, to create a single commit message that describes the change.
- A release branch or the default branch must be merged into another release branch using `Merge Commit`, to preserve each individual commit message that describes its respective change.
- For changelog generation, only the commit message set when merging the pull request is relevant. The title and description of the GitHub pull request as authored by the contributor have no influence on the changelog generation. However, the title of the GitHub pull request should be used as the commit message. See the following chapters for considerations in special scenarios, e.g. merging a breaking change or reverting a commit.
### Breaking Change
If the pull request contains a breaking change, the commit message must contain the phrase `BREAKING CHANGE`, capitalized and without any formatting, followed by a short description of the breaking change and ideally how the developer should address it, all in a single line. This line should contain more details focusing on the "breaking” aspect of the change and is intended to assist the developer in adapting. Keep it concise, as it will become part of the changelog entry, for example:
```
fix: remove handle from door
@@ -344,8 +354,55 @@ The following guide is for anyone who merges a contributor pull request into the
BREAKING CHANGE: You cannot open the door anymore by using a handle. See the [#migration guide](http://example.com) for more details.
```
Keep in mind that in a repository with release automation, merging such a commit message will trigger a release with a major version increment.
- A contributor pull request must be merged into the working branch using `Squash and Merge`, to create a single commit message that describes the change.
- A release branch or the default branch must be merged into another release branch using `Merge Commit`, to preserve each individual commit message that describes its respective change.
### Reverting
If the commit reverts a previous commit, use the prefix `revert:`, followed by the header of the reverted commit. In the body of the commit message add `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted. For example:
```
revert: fix: remove handle from door
This reverts commit 1234567890abcdef.
```
⚠️ A `revert` prefix will *always* trigger a release. Generally, a commit that did not trigger a release when it was initially merged should also not trigger a release when it is reverted. For example, do not use the `revert` prefix when reverting a commit that has a `ci` prefix:
```
ci: add something
```
is reverted with:
```
ci: remove something
```
instead of:
```
revert: ci: add something
This reverts commit 1234567890abcdef.
```
## Releasing
### General Considerations
- The `package-lock.json` file has to be deleted and recreated by npm from scratch in regular intervals using the `npm i` command. It is not enough to only update the file via automated security pull requests (e.g. dependabot, snyk), that can create inconsistencies between sub-devependencies of a dependency and increase the chances of vulnerabilities. The file should be recreated once every release cycle which is usually monthly.
### Major Release / Long-Term-Support
Long-Term-Support (LTS) is provided for the previous Parse Server major version. For example, Parse Server 4.x will receive security updates until Parse Server 5.x is superseded by Parse Server 6.x and becomes the new LTS version. While the current major version is published on branch `release`, a LTS version is published on branch `release-#.x.x`, for example `release-4.x.x` for the Parse Server 4.x LTS branch.
#### Preparing Release
The following changes are done in the `alpha` branch, before publishing the last `beta` version that will eventually become the major release. This way the changes trickle naturally through all branches and code consistency is ensured among branches.
- Make sure all [deprecations](https://github.com/parse-community/parse-server/blob/alpha/DEPRECATIONS.md) are reflected in code, old code is removed and the deprecations table is updated.
- Add the future LTS branch `release-#.x.x` to the branch list in [release.config.js](https://github.com/parse-community/parse-server/blob/alpha/release.config.js) so that the branch will later be recognized for release automation.
#### Publishing Release
1. Create LTS branch `release-#.x.x` off the latest version tag on `release` branch.
2. Create temporary branch `build-release` off branch `beta` and create a pull request with `release` as the base branch.
3. Merge branch `build-release` into `release`. Given that there will be breaking changes, a new major release will be created. In the unlikely case that there have been no breaking changes between the previous major release and the upcoming release, a major version increment has to be triggered manually. See the docs of the release automation framework for how to do that.
## Versioning
+94
View File
@@ -1,3 +1,97 @@
# [5.2.0-alpha.3](https://github.com/parse-community/parse-server/compare/5.2.0-alpha.2...5.2.0-alpha.3) (2022-03-24)
### Bug Fixes
* security bump minimist from 1.2.5 to 1.2.6 ([#7884](https://github.com/parse-community/parse-server/issues/7884)) ([c5cf282](https://github.com/parse-community/parse-server/commit/c5cf282d11ffdc023764f8e7539a2bd6bc246fe1))
# [5.2.0-alpha.2](https://github.com/parse-community/parse-server/compare/5.2.0-alpha.1...5.2.0-alpha.2) (2022-03-24)
### Bug Fixes
* sensitive keyword detection may produce false positives ([#7881](https://github.com/parse-community/parse-server/issues/7881)) ([0d6f9e9](https://github.com/parse-community/parse-server/commit/0d6f9e951d9e186e95e96d8869066ce7022bad02))
# [5.2.0-alpha.1](https://github.com/parse-community/parse-server/compare/5.1.1...5.2.0-alpha.1) (2022-03-23)
### Features
* improved LiveQuery error logging with additional information ([#7837](https://github.com/parse-community/parse-server/issues/7837)) ([443a509](https://github.com/parse-community/parse-server/commit/443a5099059538d379fe491793a5871fcbb4f377))
# [5.0.0-alpha.29](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.28...5.0.0-alpha.29) (2022-03-12)
### Features
* bump required node engine to >=12.22.10 ([#7846](https://github.com/parse-community/parse-server/issues/7846)) ([5ace99d](https://github.com/parse-community/parse-server/commit/5ace99d542a11e422af46d9fd6b1d3d2513b34cf))
### BREAKING CHANGES
* This requires Node.js version >=12.22.10. ([5ace99d](5ace99d))
# [5.0.0-alpha.28](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.27...5.0.0-alpha.28) (2022-03-12)
### Bug Fixes
* security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) ([#7844](https://github.com/parse-community/parse-server/issues/7844)) ([e569f40](https://github.com/parse-community/parse-server/commit/e569f402b1fd8648fb0d1523b71b2a03273902a5))
# [5.0.0-alpha.27](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.26...5.0.0-alpha.27) (2022-03-12)
### Reverts
* update node engine to 2.22.0 ([#7827](https://github.com/parse-community/parse-server/issues/7827)) ([f235412](https://github.com/parse-community/parse-server/commit/f235412c1b6c2b173b7531f285429ea7214b56a2))
# [5.0.0-alpha.26](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.25...5.0.0-alpha.26) (2022-02-25)
### Bug Fixes
* package.json & package-lock.json to reduce vulnerabilities ([#7823](https://github.com/parse-community/parse-server/issues/7823)) ([5ca2288](https://github.com/parse-community/parse-server/commit/5ca228882332b65f3ac05407e6e4da1ee3ef3749))
# [5.0.0-alpha.25](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.24...5.0.0-alpha.25) (2022-02-23)
### Bug Fixes
* upgrade winston from 3.5.0 to 3.5.1 ([#7820](https://github.com/parse-community/parse-server/issues/7820)) ([4af253d](https://github.com/parse-community/parse-server/commit/4af253d1f8654a6f57b5137ad310cdacadc922cc))
# [5.0.0-alpha.24](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.23...5.0.0-alpha.24) (2022-02-10)
### Bug Fixes
* security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7801](https://github.com/parse-community/parse-server/issues/7801)) ([70088a9](https://github.com/parse-community/parse-server/commit/70088a95a78393da2a4ac68be81e63107747626a))
# [5.0.0-alpha.23](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.22...5.0.0-alpha.23) (2022-02-06)
### Bug Fixes
* server crash using GraphQL due to missing @apollo/client peer dependency ([#7787](https://github.com/parse-community/parse-server/issues/7787)) ([08089d6](https://github.com/parse-community/parse-server/commit/08089d6fcbb215412448ce7d92b21b9fe6c929f2))
# [5.0.0-alpha.22](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.21...5.0.0-alpha.22) (2022-02-06)
### Features
* upgrade to MongoDB Node.js driver 4.x for MongoDB 5.0 support ([#7794](https://github.com/parse-community/parse-server/issues/7794)) ([f88aa2a](https://github.com/parse-community/parse-server/commit/f88aa2a62a533e5344d1c13dd38c5a0b283a480a))
### BREAKING CHANGES
* The MongoDB GridStore adapter has been removed. By default, Parse Server already uses GridFS, so if you do not manually use the GridStore adapter, you can ignore this change. ([f88aa2a](f88aa2a))
# [5.0.0-alpha.21](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.20...5.0.0-alpha.21) (2022-01-25)
### Features
* add Cloud Code context to `ParseObject.fetch` ([#7779](https://github.com/parse-community/parse-server/issues/7779)) ([315290d](https://github.com/parse-community/parse-server/commit/315290d16110110938f80a6b779cc2d1db58c552))
# [5.0.0-alpha.20](https://github.com/parse-community/parse-server/compare/5.0.0-alpha.19...5.0.0-alpha.20) (2022-01-22)
+191
View File
@@ -0,0 +1,191 @@
# [5.2.0-beta.2](https://github.com/parse-community/parse-server/compare/5.2.0-beta.1...5.2.0-beta.2) (2022-03-24)
### Bug Fixes
* security bump minimist from 1.2.5 to 1.2.6 ([#7884](https://github.com/parse-community/parse-server/issues/7884)) ([c5cf282](https://github.com/parse-community/parse-server/commit/c5cf282d11ffdc023764f8e7539a2bd6bc246fe1))
* sensitive keyword detection may produce false positives ([#7881](https://github.com/parse-community/parse-server/issues/7881)) ([0d6f9e9](https://github.com/parse-community/parse-server/commit/0d6f9e951d9e186e95e96d8869066ce7022bad02))
# [5.2.0-beta.1](https://github.com/parse-community/parse-server/compare/5.1.1...5.2.0-beta.1) (2022-03-23)
### Features
* improved LiveQuery error logging with additional information ([#7837](https://github.com/parse-community/parse-server/issues/7837)) ([443a509](https://github.com/parse-community/parse-server/commit/443a5099059538d379fe491793a5871fcbb4f377))
# [5.0.0-beta.10](https://github.com/parse-community/parse-server/compare/5.0.0-beta.9...5.0.0-beta.10) (2022-03-15)
### Bug Fixes
* adding or modifying a nested property requires addField permissions ([#7679](https://github.com/parse-community/parse-server/issues/7679)) ([6a6248b](https://github.com/parse-community/parse-server/commit/6a6248b6cb2e732d17131e18e659943b894ed2f1))
* bump nanoid from 3.1.25 to 3.2.0 ([#7781](https://github.com/parse-community/parse-server/issues/7781)) ([f5f63bf](https://github.com/parse-community/parse-server/commit/f5f63bfc64d3481ed944ceb5e9f50b33dccd1ce9))
* bump node-fetch from 2.6.1 to 3.1.1 ([#7782](https://github.com/parse-community/parse-server/issues/7782)) ([9082351](https://github.com/parse-community/parse-server/commit/90823514113a1a085ebc818f7109b3fd7591346f))
* node engine compatibility did not include node 16 ([#7739](https://github.com/parse-community/parse-server/issues/7739)) ([ea7c014](https://github.com/parse-community/parse-server/commit/ea7c01400f992a1263543706fe49b6174758a2d6))
* node engine range has no upper limit to exclude incompatible node versions ([#7692](https://github.com/parse-community/parse-server/issues/7692)) ([573558d](https://github.com/parse-community/parse-server/commit/573558d3adcbcc6222c92003829867e1a73eef94))
* package.json & package-lock.json to reduce vulnerabilities ([#7823](https://github.com/parse-community/parse-server/issues/7823)) ([5ca2288](https://github.com/parse-community/parse-server/commit/5ca228882332b65f3ac05407e6e4da1ee3ef3749))
* schema cache not cleared in some cases ([#7678](https://github.com/parse-community/parse-server/issues/7678)) ([5af6e5d](https://github.com/parse-community/parse-server/commit/5af6e5dfaa129b1a350afcba4fb381b21c4cc35d))
* security upgrade follow-redirects from 1.14.6 to 1.14.7 ([#7769](https://github.com/parse-community/parse-server/issues/7769)) ([8f5a861](https://github.com/parse-community/parse-server/commit/8f5a8618cfa7ed9a2a239a095abffa8f3fd8d31a))
* security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7801](https://github.com/parse-community/parse-server/issues/7801)) ([70088a9](https://github.com/parse-community/parse-server/commit/70088a95a78393da2a4ac68be81e63107747626a))
* security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) ([#7844](https://github.com/parse-community/parse-server/issues/7844)) ([e569f40](https://github.com/parse-community/parse-server/commit/e569f402b1fd8648fb0d1523b71b2a03273902a5))
* server crash using GraphQL due to missing @apollo/client peer dependency ([#7787](https://github.com/parse-community/parse-server/issues/7787)) ([08089d6](https://github.com/parse-community/parse-server/commit/08089d6fcbb215412448ce7d92b21b9fe6c929f2))
* unable to use objectId size higher than 19 on GraphQL API ([#7627](https://github.com/parse-community/parse-server/issues/7627)) ([ed86c80](https://github.com/parse-community/parse-server/commit/ed86c807721cc52a1a5a9dea0b768717eec269ed))
* upgrade mime from 2.5.2 to 3.0.0 ([#7725](https://github.com/parse-community/parse-server/issues/7725)) ([f5ef98b](https://github.com/parse-community/parse-server/commit/f5ef98bde32083403c0e30a12162fcc1e52cac37))
* upgrade parse from 3.3.1 to 3.4.0 ([#7723](https://github.com/parse-community/parse-server/issues/7723)) ([d4c1f47](https://github.com/parse-community/parse-server/commit/d4c1f473073764cb0570c633fc4a30669c2ce889))
* upgrade winston from 3.5.0 to 3.5.1 ([#7820](https://github.com/parse-community/parse-server/issues/7820)) ([4af253d](https://github.com/parse-community/parse-server/commit/4af253d1f8654a6f57b5137ad310cdacadc922cc))
### Features
* add Cloud Code context to `ParseObject.fetch` ([#7779](https://github.com/parse-community/parse-server/issues/7779)) ([315290d](https://github.com/parse-community/parse-server/commit/315290d16110110938f80a6b779cc2d1db58c552))
* add Idempotency to Postgres ([#7750](https://github.com/parse-community/parse-server/issues/7750)) ([0c3feaa](https://github.com/parse-community/parse-server/commit/0c3feaaa1751964c0db89f25674935c3354b1538))
* add support for Node 16 ([#7707](https://github.com/parse-community/parse-server/issues/7707)) ([45cc58c](https://github.com/parse-community/parse-server/commit/45cc58c7e5e640a46c5d508019a3aa81242964b1))
* bump required node engine to >=12.22.10 ([#7846](https://github.com/parse-community/parse-server/issues/7846)) ([5ace99d](https://github.com/parse-community/parse-server/commit/5ace99d542a11e422af46d9fd6b1d3d2513b34cf))
* support `postgresql` protocol in database URI ([#7757](https://github.com/parse-community/parse-server/issues/7757)) ([caf4a23](https://github.com/parse-community/parse-server/commit/caf4a2341f554b28e3918c53e7e897a3ca47bf8b))
* support relativeTime query constraint on Postgres ([#7747](https://github.com/parse-community/parse-server/issues/7747)) ([16b1b2a](https://github.com/parse-community/parse-server/commit/16b1b2a19714535ca805f2dbb3b561d8f6a519a7))
* upgrade to MongoDB Node.js driver 4.x for MongoDB 5.0 support ([#7794](https://github.com/parse-community/parse-server/issues/7794)) ([f88aa2a](https://github.com/parse-community/parse-server/commit/f88aa2a62a533e5344d1c13dd38c5a0b283a480a))
### Reverts
* refactor: allow ES import for cloud string if package type is module ([b64640c](https://github.com/parse-community/parse-server/commit/b64640c5705f733798783e68d216e957044ef23c))
* update node engine to 2.22.0 ([#7827](https://github.com/parse-community/parse-server/issues/7827)) ([f235412](https://github.com/parse-community/parse-server/commit/f235412c1b6c2b173b7531f285429ea7214b56a2))
### BREAKING CHANGES
* This requires Node.js version >=12.22.10. ([5ace99d](5ace99d))
* The MongoDB GridStore adapter has been removed. By default, Parse Server already uses GridFS, so if you do not manually use the GridStore adapter, you can ignore this change. ([f88aa2a](f88aa2a))
* Removes official Node 15 support which has reached it end-of-life date. ([45cc58c](45cc58c))
# [5.0.0-beta.9](https://github.com/parse-community/parse-server/compare/5.0.0-beta.8...5.0.0-beta.9) (2022-03-12)
### Features
* bump required node engine to >=12.22.10 ([#7848](https://github.com/parse-community/parse-server/issues/7848)) ([23a3488](https://github.com/parse-community/parse-server/commit/23a3488f15511fafbe0e1d7ff0ef8355f9cb0215))
### BREAKING CHANGES
* This requires Node.js version >=12.22.10. ([23a3488](23a3488))
# [5.0.0-beta.8](https://github.com/parse-community/parse-server/compare/5.0.0-beta.7...5.0.0-beta.8) (2022-03-12)
### Bug Fixes
* security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) ([#7843](https://github.com/parse-community/parse-server/issues/7843)) ([971adb5](https://github.com/parse-community/parse-server/commit/971adb54387b0ede31be05ca407d5f35b4575c83))
# [5.0.0-beta.7](https://github.com/parse-community/parse-server/compare/5.0.0-beta.6...5.0.0-beta.7) (2022-02-10)
### Bug Fixes
* security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7802](https://github.com/parse-community/parse-server/issues/7802)) ([7029b27](https://github.com/parse-community/parse-server/commit/7029b274ca87bc8058617f29865d683dc3b351a1))
# [5.0.0-beta.6](https://github.com/parse-community/parse-server/compare/5.0.0-beta.5...5.0.0-beta.6) (2022-01-13)
### Bug Fixes
* security upgrade follow-redirects from 1.14.2 to 1.14.7 ([#7772](https://github.com/parse-community/parse-server/issues/7772)) ([4bd34b1](https://github.com/parse-community/parse-server/commit/4bd34b189bc9f5aa2e70b7e7c1a456e91b6de773))
# [5.0.0-beta.5](https://github.com/parse-community/parse-server/compare/5.0.0-beta.4...5.0.0-beta.5) (2022-01-13)
### Bug Fixes
* schema cache not cleared in some cases ([#7771](https://github.com/parse-community/parse-server/issues/7771)) ([3b92fa1](https://github.com/parse-community/parse-server/commit/3b92fa1ca9e8889127a32eba913d68309397ca2c))
# [5.0.0-beta.4](https://github.com/parse-community/parse-server/compare/5.0.0-beta.3...5.0.0-beta.4) (2021-11-27)
### Bug Fixes
* unable to use objectId size higher than 19 on GraphQL API ([#7722](https://github.com/parse-community/parse-server/issues/7722)) ([8ee0445](https://github.com/parse-community/parse-server/commit/8ee0445c0aeeb88dff2559b46ade408071d22143))
# [5.0.0-beta.3](https://github.com/parse-community/parse-server/compare/5.0.0-beta.2...5.0.0-beta.3) (2021-11-12)
### Bug Fixes
* node engine range has no upper limit to exclude incompatible node versions ([#7693](https://github.com/parse-community/parse-server/issues/7693)) ([6a54dac](https://github.com/parse-community/parse-server/commit/6a54dac24d9fb63a44f311b8d414f4aa64140f32))
# [5.0.0-beta.2](https://github.com/parse-community/parse-server/compare/5.0.0-beta.1...5.0.0-beta.2) (2021-11-10)
### Reverts
* refactor: allow ES import for cloud string if package type is module ([#7691](https://github.com/parse-community/parse-server/issues/7691)) ([200d4ba](https://github.com/parse-community/parse-server/commit/200d4ba9a527016a65668738c7728696f443bd53))
# [5.0.0-beta.1](https://github.com/parse-community/parse-server/compare/4.5.0...5.0.0-beta.1) (2021-11-01)
### BREAKING CHANGES
- Improved schema caching through database real-time hooks. Reduces DB queries, decreases Parse Query execution time and fixes a potential schema memory leak. If multiple Parse Server instances connect to the same DB (for example behind a load balancer), set the [Parse Server Option](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) `databaseOptions.enableSchemaHooks: true` to enable this feature and keep the schema in sync across all instances. Failing to do so will cause a schema change to not propagate to other instances and re-syncing will only happen when these instances restart. The options `enableSingleSchemaCache` and `schemaCacheTTL` have been removed. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required. (Diamond Lewis, SebC) [#7214](https://github.com/parse-community/parse-server/issues/7214)
- Added file upload restriction. File upload is now only allowed for authenticated users by default for improved security. To allow file upload also for Anonymous Users or Public, set the `fileUpload` parameter in the [Parse Server Options](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) (dblythy, Manuel Trezza) [#7071](https://github.com/parse-community/parse-server/pull/7071)
- Removed [parse-server-simple-mailgun-adapter](https://github.com/parse-community/parse-server-simple-mailgun-adapter) dependency; to continue using the adapter it has to be explicitly installed (Manuel Trezza) [#7321](https://github.com/parse-community/parse-server/pull/7321)
- Remove support for MongoDB 3.6 which has reached its End-of-Life date and PostgreSQL 10 (Manuel Trezza) [#7315](https://github.com/parse-community/parse-server/pull/7315)
- Remove support for Node 10 which has reached its End-of-Life date (Manuel Trezza) [#7314](https://github.com/parse-community/parse-server/pull/7314)
- Remove S3 Files Adapter from Parse Server, instead install separately as `@parse/s3-files-adapter` (Manuel Trezza) [#7324](https://github.com/parse-community/parse-server/pull/7324)
- Remove Session field `restricted`; the field was a code artifact from a feature that never existed in Open Source Parse Server; if you have been using this field for custom purposes, consider that for new Parse Server installations the field does not exist anymore in the schema, and for existing installations the field default value `false` will not be set anymore when creating a new session (Manuel Trezza) [#7543](https://github.com/parse-community/parse-server/pull/7543)
- ci: add node engine version check (Manuel Trezza) [#7574](https://github.com/parse-community/parse-server/pull/7574)
- To delete a field via the GraphQL API, the field value has to be set to `null`. Previously, setting a field value to `null` would save a null value in the database, which was not according to the [GraphQL specs](https://spec.graphql.org/June2018/#sec-Null-Value). To delete a file field use `file: null`, the previous way of using `file: { file: null }` has become obsolete. ([626fad2](626fad2))
### Notable Changes
- Alphabetical ordered GraphQL API, improved GraphQL Schema cache system and fix GraphQL input reassign issue (Moumouls) [#7344](https://github.com/parse-community/parse-server/issues/7344)
- Added Parse Server Security Check to report weak security settings (Manuel Trezza, dblythy) [#7247](https://github.com/parse-community/parse-server/issues/7247)
- EXPERIMENTAL: Added new page router with placeholder rendering and localization of custom and feature pages such as password reset and email verification (Manuel Trezza) [#7128](https://github.com/parse-community/parse-server/pull/7128)
- EXPERIMENTAL: Added custom routes to easily customize flows for password reset, email verification or build entirely new flows (Manuel Trezza) [#7231](https://github.com/parse-community/parse-server/pull/7231)
- Added Deprecation Policy to govern the introduction of breaking changes in a phased pattern that is more predictable for developers (Manuel Trezza) [#7199](https://github.com/parse-community/parse-server/pull/7199)
- Add REST API endpoint `/loginAs` to create session of any user with master key; allows to impersonate another user. (GormanFletcher) [#7406](https://github.com/parse-community/parse-server/pull/7406)
- Add official support for MongoDB 5.0 (Manuel Trezza) [#7469](https://github.com/parse-community/parse-server/pull/7469)
- Added Parse Server Configuration `enforcePrivateUsers`, which will remove public access by default on new Parse.Users (dblythy) [#7319](https://github.com/parse-community/parse-server/pull/7319)
* add support for Postgres 14 ([#7644](https://github.com/parse-community/parse-server/issues/7644)) ([090350a](https://github.com/parse-community/parse-server/commit/090350a7a0fac945394ca1cb24b290316ef06aa7))
* add user-defined schema and migrations ([#7418](https://github.com/parse-community/parse-server/issues/7418)) ([25d5c30](https://github.com/parse-community/parse-server/commit/25d5c30be2111be332eb779eb0697774a17da7af))
* setting a field to null does not delete it via GraphQL API ([#7649](https://github.com/parse-community/parse-server/issues/7649)) ([626fad2](https://github.com/parse-community/parse-server/commit/626fad2e71017dcc62196c487de5f908fa43000b))
* combined `and` query with relational query condition returns incorrect results ([#7593](https://github.com/parse-community/parse-server/issues/7593)) ([174886e](https://github.com/parse-community/parse-server/commit/174886e385e091c6bbd4a84891ef95f80b50d05c))
### Other Changes
- Support native mongodb syntax in aggregation pipelines (Raschid JF Rafeally) [#7339](https://github.com/parse-community/parse-server/pull/7339)
- Fix error when a not yet inserted job is updated (Antonio Davi Macedo Coelho de Castro) [#7196](https://github.com/parse-community/parse-server/pull/7196)
- request.context for afterFind triggers (dblythy) [#7078](https://github.com/parse-community/parse-server/pull/7078)
- Winston Logger interpolating stdout to console (dplewis) [#7114](https://github.com/parse-community/parse-server/pull/7114)
- Added convenience method `Parse.Cloud.sendEmail(...)` to send email via email adapter in Cloud Code (dblythy) [#7089](https://github.com/parse-community/parse-server/pull/7089)
- LiveQuery support for $and, $nor, $containedBy, $geoWithin, $geoIntersects queries (dplewis) [#7113](https://github.com/parse-community/parse-server/pull/7113)
- Supporting patterns in LiveQuery server's config parameter `classNames` (Nes-si) [#7131](https://github.com/parse-community/parse-server/pull/7131)
- Added `requireAnyUserRoles` and `requireAllUserRoles` for Parse Cloud validator (dblythy) [#7097](https://github.com/parse-community/parse-server/pull/7097)
- Support Facebook Limited Login (miguel-s) [#7219](https://github.com/parse-community/parse-server/pull/7219)
- Removed Stage name check on aggregate pipelines (BRETT71) [#7237](https://github.com/parse-community/parse-server/pull/7237)
- Retry transactions on MongoDB when it fails due to transient error (Antonio Davi Macedo Coelho de Castro) [#7187](https://github.com/parse-community/parse-server/pull/7187)
- Bump tests to use Mongo 4.4.4 (Antonio Davi Macedo Coelho de Castro) [#7184](https://github.com/parse-community/parse-server/pull/7184)
- Added new account lockout policy option `accountLockout.unlockOnPasswordReset` to automatically unlock account on password reset (Manuel Trezza) [#7146](https://github.com/parse-community/parse-server/pull/7146)
- Test Parse Server continuously against all recent MongoDB versions that have not reached their end-of-life support date, added MongoDB compatibility table to Parse Server docs (Manuel Trezza) [#7161](https://github.com/parse-community/parse-server/pull/7161)
- Test Parse Server continuously against all recent Node.js versions that have not reached their end-of-life support date, added Node.js compatibility table to Parse Server docs (Manuel Trezza) [7161](https://github.com/parse-community/parse-server/pull/7177)
- Throw error on invalid Cloud Function validation configuration (dblythy) [#7154](https://github.com/parse-community/parse-server/pull/7154)
- Allow Cloud Validator `options` to be async (dblythy) [#7155](https://github.com/parse-community/parse-server/pull/7155)
- Optimize queries on classes with pointer permissions (Pedro Diaz) [#7061](https://github.com/parse-community/parse-server/pull/7061)
- Test Parse Server continuously against all relevant Postgres versions (minor versions), added Postgres compatibility table to Parse Server docs (Corey Baker) [#7176](https://github.com/parse-community/parse-server/pull/7176)
- Randomize test suite (Diamond Lewis) [#7265](https://github.com/parse-community/parse-server/pull/7265)
- LDAP: Properly unbind client on group search error (Diamond Lewis) [#7265](https://github.com/parse-community/parse-server/pull/7265)
- Improve data consistency in Push and Job Status update (Diamond Lewis) [#7267](https://github.com/parse-community/parse-server/pull/7267)
- Excluding keys that have trailing edges.node when performing GraphQL resolver (Chris Bland) [#7273](https://github.com/parse-community/parse-server/pull/7273)
- Added centralized feature deprecation with standardized warning logs (Manuel Trezza) [#7303](https://github.com/parse-community/parse-server/pull/7303)
- Use Node.js 15.13.0 in CI (Olle Jonsson) [#7312](https://github.com/parse-community/parse-server/pull/7312)
- Fix file upload issue for S3 compatible storage (Linode, DigitalOcean) by avoiding empty tags property when creating a file (Ali Oguzhan Yildiz) [#7300](https://github.com/parse-community/parse-server/pull/7300)
- Add building Docker image as CI check (Manuel Trezza) [#7332](https://github.com/parse-community/parse-server/pull/7332)
- Add NPM package-lock version check to CI (Manuel Trezza) [#7333](https://github.com/parse-community/parse-server/pull/7333)
- Fix incorrect LiveQuery events triggered for multiple subscriptions on the same class with different events [#7341](https://github.com/parse-community/parse-server/pull/7341)
- Fix select and excludeKey queries to properly accept JSON string arrays. Also allow nested fields in exclude (Corey Baker) [#7242](https://github.com/parse-community/parse-server/pull/7242)
- Fix LiveQuery server crash when using $all query operator on a missing object key (Jason Posthuma) [#7421](https://github.com/parse-community/parse-server/pull/7421)
- Added runtime deprecation warnings (Manuel Trezza) [#7451](https://github.com/parse-community/parse-server/pull/7451)
- Add ability to pass context of an object via a header, X-Parse-Cloud-Context, for Cloud Code triggers. The header addition allows client SDK's to add context without injecting _context in the body of JSON objects (Corey Baker) [#7437](https://github.com/parse-community/parse-server/pull/7437)
- Add CI check to add changelog entry (Manuel Trezza) [#7512](https://github.com/parse-community/parse-server/pull/7512)
- Refactor: uniform issue templates across repos (Manuel Trezza) [#7528](https://github.com/parse-community/parse-server/pull/7528)
- ci: bump ci environment (Manuel Trezza) [#7539](https://github.com/parse-community/parse-server/pull/7539)
- CI now pushes docker images to Docker Hub (Corey Baker) [#7548](https://github.com/parse-community/parse-server/pull/7548)
- Allow afterFind and afterLiveQueryEvent to set unsaved pointers and keys (dblythy) [#7310](https://github.com/parse-community/parse-server/pull/7310)
- Allow setting descending sort to full text queries (dblythy) [#7496](https://github.com/parse-community/parse-server/pull/7496)
- Allow cloud string for ES modules (Daniel Blyth) [#7560](https://github.com/parse-community/parse-server/pull/7560)
- docs: Introduce deprecation ID for reference in comments and online search (Manuel Trezza) [#7562](https://github.com/parse-community/parse-server/pull/7562)
- refactor: deprecate `Parse.Cloud.httpRequest`; it is recommended to use a HTTP library instead. (Daniel Blyth) [#7595](https://github.com/parse-community/parse-server/pull/7595)
- refactor: Modernize HTTPRequest tests (brandongregoryscott) [#7604](https://github.com/parse-community/parse-server/pull/7604)
- Allow liveQuery on Session class (Daniel Blyth) [#7554](https://github.com/parse-community/parse-server/pull/7554)
+181
View File
@@ -1,3 +1,184 @@
## [5.2.2](https://github.com/parse-community/parse-server/compare/5.2.1...5.2.2) (2022-06-17)
### Bug Fixes
* certificate in Apple Game Center auth adapter not validated; this fixes a security vulnerability in which authentication could be bypassed using a fake certificate; if you are using the Apple Gamer Center auth adapter it is your responsibility to keep its root certificate up-to-date and we advice you read the security advisory ([GHSA-rh9j-f5f8-rvgc](https://github.com/parse-community/parse-server/security/advisories/GHSA-rh9j-f5f8-rvgc)) ([ba2b0a9](https://github.com/parse-community/parse-server/commit/ba2b0a9cb9a568817a114b132a4c2e0911d76df1))
## [5.2.1](https://github.com/parse-community/parse-server/compare/5.2.0...5.2.1) (2022-05-01)
### Bug Fixes
* authentication bypass and denial of service (DoS) vulnerabilities in Apple Game Center auth adapter (GHSA-qf8x-vqjv-92gr) ([#7962](https://github.com/parse-community/parse-server/issues/7962)) ([af4a041](https://github.com/parse-community/parse-server/commit/af4a0417a9f3c1e99b3793806b4b18e04d9fa999))
# [5.2.0](https://github.com/parse-community/parse-server/compare/5.1.1...5.2.0) (2022-03-24)
### Bug Fixes
* security bump minimist from 1.2.5 to 1.2.6 ([#7884](https://github.com/parse-community/parse-server/issues/7884)) ([c5cf282](https://github.com/parse-community/parse-server/commit/c5cf282d11ffdc023764f8e7539a2bd6bc246fe1))
* sensitive keyword detection may produce false positives ([#7881](https://github.com/parse-community/parse-server/issues/7881)) ([0d6f9e9](https://github.com/parse-community/parse-server/commit/0d6f9e951d9e186e95e96d8869066ce7022bad02))
### Features
* improved LiveQuery error logging with additional information ([#7837](https://github.com/parse-community/parse-server/issues/7837)) ([443a509](https://github.com/parse-community/parse-server/commit/443a5099059538d379fe491793a5871fcbb4f377))
## [5.1.1](https://github.com/parse-community/parse-server/compare/5.1.0...5.1.1) (2022-03-18)
### Reverts
* ci: temporarily disable breaking change detection ([#7861](https://github.com/parse-community/parse-server/issues/7861)) ([effed92](https://github.com/parse-community/parse-server/commit/effed92cabd88676fdf9eca2e079a4d8be017f1b))
# [5.1.0](https://github.com/parse-community/parse-server/compare/5.0.0...5.1.0) (2022-03-18)
### Bug Fixes
* adding or modifying a nested property requires addField permissions ([#7679](https://github.com/parse-community/parse-server/issues/7679)) ([6a6248b](https://github.com/parse-community/parse-server/commit/6a6248b6cb2e732d17131e18e659943b894ed2f1))
* bump nanoid from 3.1.25 to 3.2.0 ([#7781](https://github.com/parse-community/parse-server/issues/7781)) ([f5f63bf](https://github.com/parse-community/parse-server/commit/f5f63bfc64d3481ed944ceb5e9f50b33dccd1ce9))
* bump node-fetch from 2.6.1 to 3.1.1 ([#7782](https://github.com/parse-community/parse-server/issues/7782)) ([9082351](https://github.com/parse-community/parse-server/commit/90823514113a1a085ebc818f7109b3fd7591346f))
* node engine compatibility did not include node 16 ([#7739](https://github.com/parse-community/parse-server/issues/7739)) ([ea7c014](https://github.com/parse-community/parse-server/commit/ea7c01400f992a1263543706fe49b6174758a2d6))
* node engine range has no upper limit to exclude incompatible node versions ([#7692](https://github.com/parse-community/parse-server/issues/7692)) ([573558d](https://github.com/parse-community/parse-server/commit/573558d3adcbcc6222c92003829867e1a73eef94))
* package.json & package-lock.json to reduce vulnerabilities ([#7823](https://github.com/parse-community/parse-server/issues/7823)) ([5ca2288](https://github.com/parse-community/parse-server/commit/5ca228882332b65f3ac05407e6e4da1ee3ef3749))
* schema cache not cleared in some cases ([#7678](https://github.com/parse-community/parse-server/issues/7678)) ([5af6e5d](https://github.com/parse-community/parse-server/commit/5af6e5dfaa129b1a350afcba4fb381b21c4cc35d))
* security upgrade follow-redirects from 1.14.6 to 1.14.7 ([#7769](https://github.com/parse-community/parse-server/issues/7769)) ([8f5a861](https://github.com/parse-community/parse-server/commit/8f5a8618cfa7ed9a2a239a095abffa8f3fd8d31a))
* security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7801](https://github.com/parse-community/parse-server/issues/7801)) ([70088a9](https://github.com/parse-community/parse-server/commit/70088a95a78393da2a4ac68be81e63107747626a))
* security vulnerability that allows remote code execution (GHSA-p6h4-93qp-jhcm) ([#7844](https://github.com/parse-community/parse-server/issues/7844)) ([e569f40](https://github.com/parse-community/parse-server/commit/e569f402b1fd8648fb0d1523b71b2a03273902a5))
* server crash using GraphQL due to missing @apollo/client peer dependency ([#7787](https://github.com/parse-community/parse-server/issues/7787)) ([08089d6](https://github.com/parse-community/parse-server/commit/08089d6fcbb215412448ce7d92b21b9fe6c929f2))
* unable to use objectId size higher than 19 on GraphQL API ([#7627](https://github.com/parse-community/parse-server/issues/7627)) ([ed86c80](https://github.com/parse-community/parse-server/commit/ed86c807721cc52a1a5a9dea0b768717eec269ed))
* upgrade mime from 2.5.2 to 3.0.0 ([#7725](https://github.com/parse-community/parse-server/issues/7725)) ([f5ef98b](https://github.com/parse-community/parse-server/commit/f5ef98bde32083403c0e30a12162fcc1e52cac37))
* upgrade parse from 3.3.1 to 3.4.0 ([#7723](https://github.com/parse-community/parse-server/issues/7723)) ([d4c1f47](https://github.com/parse-community/parse-server/commit/d4c1f473073764cb0570c633fc4a30669c2ce889))
* upgrade winston from 3.5.0 to 3.5.1 ([#7820](https://github.com/parse-community/parse-server/issues/7820)) ([4af253d](https://github.com/parse-community/parse-server/commit/4af253d1f8654a6f57b5137ad310cdacadc922cc))
### Features
* add Cloud Code context to `ParseObject.fetch` ([#7779](https://github.com/parse-community/parse-server/issues/7779)) ([315290d](https://github.com/parse-community/parse-server/commit/315290d16110110938f80a6b779cc2d1db58c552))
* add Idempotency to Postgres ([#7750](https://github.com/parse-community/parse-server/issues/7750)) ([0c3feaa](https://github.com/parse-community/parse-server/commit/0c3feaaa1751964c0db89f25674935c3354b1538))
* add support for Node 16 ([#7707](https://github.com/parse-community/parse-server/issues/7707)) ([45cc58c](https://github.com/parse-community/parse-server/commit/45cc58c7e5e640a46c5d508019a3aa81242964b1))
* bump required node engine to >=12.22.10 ([#7846](https://github.com/parse-community/parse-server/issues/7846)) ([5ace99d](https://github.com/parse-community/parse-server/commit/5ace99d542a11e422af46d9fd6b1d3d2513b34cf))
* support `postgresql` protocol in database URI ([#7757](https://github.com/parse-community/parse-server/issues/7757)) ([caf4a23](https://github.com/parse-community/parse-server/commit/caf4a2341f554b28e3918c53e7e897a3ca47bf8b))
* support relativeTime query constraint on Postgres ([#7747](https://github.com/parse-community/parse-server/issues/7747)) ([16b1b2a](https://github.com/parse-community/parse-server/commit/16b1b2a19714535ca805f2dbb3b561d8f6a519a7))
* upgrade to MongoDB Node.js driver 4.x for MongoDB 5.0 support ([#7794](https://github.com/parse-community/parse-server/issues/7794)) ([f88aa2a](https://github.com/parse-community/parse-server/commit/f88aa2a62a533e5344d1c13dd38c5a0b283a480a))
### Reverts
* refactor: allow ES import for cloud string if package type is module ([b64640c](https://github.com/parse-community/parse-server/commit/b64640c5705f733798783e68d216e957044ef23c))
* update node engine to 2.22.0 ([#7827](https://github.com/parse-community/parse-server/issues/7827)) ([f235412](https://github.com/parse-community/parse-server/commit/f235412c1b6c2b173b7531f285429ea7214b56a2))
### ⚠️ NOTABLE CHANGES
*The following changes would formally require a major version increment (Parse Server 6.0), but given their low relevance they are released as part of this minor version increment (Parse Server 5.1).*
* The MongoDB GridStore adapter has been removed. By default, Parse Server already uses GridFS, so if you do not manually use the GridStore adapter, you can ignore this change. Parse Server uses the GridFSBucket adapter instead of GridStore adapter by default since 2018. ([f88aa2a](f88aa2a))
* Removes official Node 15 support which has already reached it End-of-Life date. ([45cc58c](45cc58c))
# [5.0.0](https://github.com/parse-community/parse-server/compare/4.10.7...5.0.0) (2022-03-14)
### BREAKING CHANGES
- Improved schema caching through database real-time hooks. Reduces DB queries, decreases Parse Query execution time and fixes a potential schema memory leak. If multiple Parse Server instances connect to the same DB (for example behind a load balancer), set the [Parse Server Option](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) `databaseOptions.enableSchemaHooks: true` to enable this feature and keep the schema in sync across all instances. Failing to do so will cause a schema change to not propagate to other instances and re-syncing will only happen when these instances restart. The options `enableSingleSchemaCache` and `schemaCacheTTL` have been removed. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required. (Diamond Lewis, SebC) [#7214](https://github.com/parse-community/parse-server/issues/7214)
- Fix security vulnerability that allows remote code execution; as part of the fix a new security feature scans for sensitive keywords in request data to prevent JavaScript prototype pollution. If such a keyword is found, the request is rejected with HTTP response code `400` and Parse Error `105` (`INVALID_KEY_NAME`). By default these keywords are: `{_bsontype: "Code"}`, `constructor`, `__proto__`. If you are using any of these keywords in your request data, you can override the default keywords by setting the new Parse Server option `requestKeywordDenylist` to `[]` and specify your own keywords as needed. ([GHSA-p6h4-93qp-jhcm](https://github.com/advisories/GHSA-p6h4-93qp-jhcm)) ([#7843](https://github.com/parse-community/parse-server/issues/7843)) ([971adb5](https://github.com/parse-community/parse-server/commit/971adb54387b0ede31be05ca407d5f35b4575c83))
- Added file upload restriction. File upload is now only allowed for authenticated users by default for improved security. To allow file upload also for Anonymous Users or Public, set the `fileUpload` parameter in the [Parse Server Options](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) (dblythy, Manuel Trezza) [#7071](https://github.com/parse-community/parse-server/pull/7071)
- Removed [parse-server-simple-mailgun-adapter](https://github.com/parse-community/parse-server-simple-mailgun-adapter) dependency; to continue using the adapter it has to be explicitly installed (Manuel Trezza) [#7321](https://github.com/parse-community/parse-server/pull/7321)
- Remove support for MongoDB 3.6 which has reached its End-of-Life date and PostgreSQL 10 (Manuel Trezza) [#7315](https://github.com/parse-community/parse-server/pull/7315)
- Remove support for Node 10 which has reached its End-of-Life date (Manuel Trezza) [#7314](https://github.com/parse-community/parse-server/pull/7314)
- Bump required Node engine to >=12.22.10 ([#7848](https://github.com/parse-community/parse-server/issues/7848)) ([23a3488](https://github.com/parse-community/parse-server/commit/23a3488f15511fafbe0e1d7ff0ef8355f9cb0215))
- Remove S3 Files Adapter from Parse Server, instead install separately as `@parse/s3-files-adapter` (Manuel Trezza) [#7324](https://github.com/parse-community/parse-server/pull/7324)
- Remove Session field `restricted`; the field was a code artifact from a feature that never existed in Open Source Parse Server; if you have been using this field for custom purposes, consider that for new Parse Server installations the field does not exist anymore in the schema, and for existing installations the field default value `false` will not be set anymore when creating a new session (Manuel Trezza) [#7543](https://github.com/parse-community/parse-server/pull/7543)
- To delete a field via the GraphQL API, the field value has to be set to `null`. Previously, setting a field value to `null` would save a null value in the database, which was not according to the [GraphQL specs](https://spec.graphql.org/June2018/#sec-Null-Value). To delete a file field use `file: null`, the previous way of using `file: { file: null }` has become obsolete. ([626fad2](626fad2))
### Notable Changes
- Alphabetical ordered GraphQL API, improved GraphQL Schema cache system and fix GraphQL input reassign issue (Moumouls) [#7344](https://github.com/parse-community/parse-server/issues/7344)
- Added Parse Server Security Check to report weak security settings (Manuel Trezza, dblythy) [#7247](https://github.com/parse-community/parse-server/issues/7247)
- EXPERIMENTAL: Added new page router with placeholder rendering and localization of custom and feature pages such as password reset and email verification (Manuel Trezza) [#7128](https://github.com/parse-community/parse-server/pull/7128)
- EXPERIMENTAL: Added custom routes to easily customize flows for password reset, email verification or build entirely new flows (Manuel Trezza) [#7231](https://github.com/parse-community/parse-server/pull/7231)
- Added Deprecation Policy to govern the introduction of breaking changes in a phased pattern that is more predictable for developers (Manuel Trezza) [#7199](https://github.com/parse-community/parse-server/pull/7199)
- Add REST API endpoint `/loginAs` to create session of any user with master key; allows to impersonate another user. (GormanFletcher) [#7406](https://github.com/parse-community/parse-server/pull/7406)
- Add official support for MongoDB 5.0 (Manuel Trezza) [#7469](https://github.com/parse-community/parse-server/pull/7469)
- Added Parse Server Configuration `enforcePrivateUsers`, which will remove public access by default on new Parse.Users (dblythy) [#7319](https://github.com/parse-community/parse-server/pull/7319)
- add support for Postgres 14 ([#7644](https://github.com/parse-community/parse-server/issues/7644)) ([090350a](https://github.com/parse-community/parse-server/commit/090350a7a0fac945394ca1cb24b290316ef06aa7))
- add user-defined schema and migrations ([#7418](https://github.com/parse-community/parse-server/issues/7418)) ([25d5c30](https://github.com/parse-community/parse-server/commit/25d5c30be2111be332eb779eb0697774a17da7af))
- setting a field to null does not delete it via GraphQL API ([#7649](https://github.com/parse-community/parse-server/issues/7649)) ([626fad2](https://github.com/parse-community/parse-server/commit/626fad2e71017dcc62196c487de5f908fa43000b))
- combined `and` query with relational query condition returns incorrect results ([#7593](https://github.com/parse-community/parse-server/issues/7593)) ([174886e](https://github.com/parse-community/parse-server/commit/174886e385e091c6bbd4a84891ef95f80b50d05c))
- node engine range has no upper limit to exclude incompatible node versions ([#7693](https://github.com/parse-community/parse-server/issues/7693)) ([6a54dac](https://github.com/parse-community/parse-server/commit/6a54dac24d9fb63a44f311b8d414f4aa64140f32))
- unable to use objectId size higher than 19 on GraphQL API ([#7722](https://github.com/parse-community/parse-server/issues/7722)) ([8ee0445](https://github.com/parse-community/parse-server/commit/8ee0445c0aeeb88dff2559b46ade408071d22143))
- schema cache not cleared in some cases ([#7771](https://github.com/parse-community/parse-server/issues/7771)) ([3b92fa1](https://github.com/parse-community/parse-server/commit/3b92fa1ca9e8889127a32eba913d68309397ca2c))
### Other Changes
- Support native mongodb syntax in aggregation pipelines (Raschid JF Rafeally) [#7339](https://github.com/parse-community/parse-server/pull/7339)
- Fix error when a not yet inserted job is updated (Antonio Davi Macedo Coelho de Castro) [#7196](https://github.com/parse-community/parse-server/pull/7196)
- request.context for afterFind triggers (dblythy) [#7078](https://github.com/parse-community/parse-server/pull/7078)
- Winston Logger interpolating stdout to console (dplewis) [#7114](https://github.com/parse-community/parse-server/pull/7114)
- Added convenience method `Parse.Cloud.sendEmail(...)` to send email via email adapter in Cloud Code (dblythy) [#7089](https://github.com/parse-community/parse-server/pull/7089)
- LiveQuery support for $and, $nor, $containedBy, $geoWithin, $geoIntersects queries (dplewis) [#7113](https://github.com/parse-community/parse-server/pull/7113)
- Supporting patterns in LiveQuery server's config parameter `classNames` (Nes-si) [#7131](https://github.com/parse-community/parse-server/pull/7131)
- Added `requireAnyUserRoles` and `requireAllUserRoles` for Parse Cloud validator (dblythy) [#7097](https://github.com/parse-community/parse-server/pull/7097)
- Support Facebook Limited Login (miguel-s) [#7219](https://github.com/parse-community/parse-server/pull/7219)
- Removed Stage name check on aggregate pipelines (BRETT71) [#7237](https://github.com/parse-community/parse-server/pull/7237)
- Retry transactions on MongoDB when it fails due to transient error (Antonio Davi Macedo Coelho de Castro) [#7187](https://github.com/parse-community/parse-server/pull/7187)
- Bump tests to use Mongo 4.4.4 (Antonio Davi Macedo Coelho de Castro) [#7184](https://github.com/parse-community/parse-server/pull/7184)
- Added new account lockout policy option `accountLockout.unlockOnPasswordReset` to automatically unlock account on password reset (Manuel Trezza) [#7146](https://github.com/parse-community/parse-server/pull/7146)
- Test Parse Server continuously against all recent MongoDB versions that have not reached their end-of-life support date, added MongoDB compatibility table to Parse Server docs (Manuel Trezza) [#7161](https://github.com/parse-community/parse-server/pull/7161)
- Test Parse Server continuously against all recent Node.js versions that have not reached their end-of-life support date, added Node.js compatibility table to Parse Server docs (Manuel Trezza) [7161](https://github.com/parse-community/parse-server/pull/7177)
- Throw error on invalid Cloud Function validation configuration (dblythy) [#7154](https://github.com/parse-community/parse-server/pull/7154)
- Allow Cloud Validator `options` to be async (dblythy) [#7155](https://github.com/parse-community/parse-server/pull/7155)
- Optimize queries on classes with pointer permissions (Pedro Diaz) [#7061](https://github.com/parse-community/parse-server/pull/7061)
- Test Parse Server continuously against all relevant Postgres versions (minor versions), added Postgres compatibility table to Parse Server docs (Corey Baker) [#7176](https://github.com/parse-community/parse-server/pull/7176)
- Randomize test suite (Diamond Lewis) [#7265](https://github.com/parse-community/parse-server/pull/7265)
- LDAP: Properly unbind client on group search error (Diamond Lewis) [#7265](https://github.com/parse-community/parse-server/pull/7265)
- Improve data consistency in Push and Job Status update (Diamond Lewis) [#7267](https://github.com/parse-community/parse-server/pull/7267)
- Excluding keys that have trailing edges.node when performing GraphQL resolver (Chris Bland) [#7273](https://github.com/parse-community/parse-server/pull/7273)
- Added centralized feature deprecation with standardized warning logs (Manuel Trezza) [#7303](https://github.com/parse-community/parse-server/pull/7303)
- Use Node.js 15.13.0 in CI (Olle Jonsson) [#7312](https://github.com/parse-community/parse-server/pull/7312)
- Fix file upload issue for S3 compatible storage (Linode, DigitalOcean) by avoiding empty tags property when creating a file (Ali Oguzhan Yildiz) [#7300](https://github.com/parse-community/parse-server/pull/7300)
- Add building Docker image as CI check (Manuel Trezza) [#7332](https://github.com/parse-community/parse-server/pull/7332)
- Add NPM package-lock version check to CI (Manuel Trezza) [#7333](https://github.com/parse-community/parse-server/pull/7333)
- Fix incorrect LiveQuery events triggered for multiple subscriptions on the same class with different events [#7341](https://github.com/parse-community/parse-server/pull/7341)
- Fix select and excludeKey queries to properly accept JSON string arrays. Also allow nested fields in exclude (Corey Baker) [#7242](https://github.com/parse-community/parse-server/pull/7242)
- Fix LiveQuery server crash when using $all query operator on a missing object key (Jason Posthuma) [#7421](https://github.com/parse-community/parse-server/pull/7421)
- Added runtime deprecation warnings (Manuel Trezza) [#7451](https://github.com/parse-community/parse-server/pull/7451)
- Add ability to pass context of an object via a header, X-Parse-Cloud-Context, for Cloud Code triggers. The header addition allows client SDK's to add context without injecting _context in the body of JSON objects (Corey Baker) [#7437](https://github.com/parse-community/parse-server/pull/7437)
- Add CI check to add changelog entry (Manuel Trezza) [#7512](https://github.com/parse-community/parse-server/pull/7512)
- Refactor: uniform issue templates across repos (Manuel Trezza) [#7528](https://github.com/parse-community/parse-server/pull/7528)
- ci: bump ci environment (Manuel Trezza) [#7539](https://github.com/parse-community/parse-server/pull/7539)
- CI now pushes docker images to Docker Hub (Corey Baker) [#7548](https://github.com/parse-community/parse-server/pull/7548)
- Allow afterFind and afterLiveQueryEvent to set unsaved pointers and keys (dblythy) [#7310](https://github.com/parse-community/parse-server/pull/7310)
- Allow setting descending sort to full text queries (dblythy) [#7496](https://github.com/parse-community/parse-server/pull/7496)
- Allow cloud string for ES modules (Daniel Blyth) [#7560](https://github.com/parse-community/parse-server/pull/7560)
- docs: Introduce deprecation ID for reference in comments and online search (Manuel Trezza) [#7562](https://github.com/parse-community/parse-server/pull/7562)
- refactor: deprecate `Parse.Cloud.httpRequest`; it is recommended to use a HTTP library instead. (Daniel Blyth) [#7595](https://github.com/parse-community/parse-server/pull/7595)
- refactor: Modernize HTTPRequest tests (brandongregoryscott) [#7604](https://github.com/parse-community/parse-server/pull/7604)
- Allow liveQuery on Session class (Daniel Blyth) [#7554](https://github.com/parse-community/parse-server/pull/7554)
- security upgrade follow-redirects from 1.14.2 to 1.14.7 ([#7772](https://github.com/parse-community/parse-server/issues/7772)) ([4bd34b1](https://github.com/parse-community/parse-server/commit/4bd34b189bc9f5aa2e70b7e7c1a456e91b6de773))
- security upgrade follow-redirects from 1.14.7 to 1.14.8 ([#7802](https://github.com/parse-community/parse-server/issues/7802)) ([7029b27](https://github.com/parse-community/parse-server/commit/7029b274ca87bc8058617f29865d683dc3b351a1))
- Add node engine version check (Manuel Trezza) [#7574](https://github.com/parse-community/parse-server/pull/7574)
## [4.10.7](https://github.com/parse-community/parse-server/compare/4.10.6...4.10.7) (2022-03-11)
### Bug Fixes
* security vulnerability that allows remote code execution ([GHSA-p6h4-93qp-jhcm](https://github.com/parse-community/parse-server/security/advisories/GHSA-p6h4-93qp-jhcm)) ([#7841](https://github.com/parse-community/parse-server/issues/7841)) ([886bfd7](https://github.com/parse-community/parse-server/commit/886bfd7cac69496e3f73d4bb536f0eec3cba0e4d))
Note that as part of the fix a new security feature scans for sensitive keywords in request data to prevent JavaScript prototype pollution. If such a keyword is found, the request is rejected with HTTP response code `400` and Parse Error `105` (`INVALID_KEY_NAME`). By default these keywords are: `{_bsontype: "Code"}`, `constructor`, `__proto__`. If you are using any of these keywords in your request data, you can override the default keywords by setting the new Parse Server option `requestKeywordDenylist` to `[]` and specify your own keywords as needed.
## [4.10.6](https://github.com/parse-community/parse-server/compare/4.10.5...4.10.6) (2022-02-12)
### Bug Fixes
* update graphql dependencies to work with Parse Dashboard ([#7658](https://github.com/parse-community/parse-server/issues/7658)) ([350ecde](https://github.com/parse-community/parse-server/commit/350ecdee590f1b9d721895b2c79306c01622c3fc))
## [4.10.5](https://github.com/parse-community/parse-server/compare/4.10.4...4.10.5) (2022-02-12)
### Bug Fixes
* security upgrade follow-redirects from 1.13.0 to 1.14.8 ([#7803](https://github.com/parse-community/parse-server/issues/7803)) ([611332e](https://github.com/parse-community/parse-server/commit/611332ea33831258efd3dd2f2c621c2e35fc95d3))
# [4.10.4](https://github.com/parse-community/parse-server/compare/4.10.3...4.10.4)
### Security Fixes
+396 -236
View File
File diff suppressed because it is too large Load Diff
+18 -16
View File
@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "5.0.0-alpha.20",
"version": "5.2.2",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
@@ -20,11 +20,12 @@
"license": "BSD-3-Clause",
"dependencies": {
"@apollographql/graphql-playground-html": "1.6.29",
"@graphql-tools/links": "8.2.1",
"@graphql-tools/links": "8.2.2",
"@apollo/client": "3.5.8",
"@graphql-tools/stitch": "6.2.4",
"@graphql-tools/utils": "6.2.4",
"@parse/fs-files-adapter": "1.2.1",
"@parse/push-adapter": "3.4.1",
"@parse/push-adapter": "4.1.0",
"apollo-server-express": "2.25.2",
"bcryptjs": "2.4.3",
"body-parser": "1.19.1",
@@ -32,7 +33,7 @@
"cors": "2.8.5",
"deepcopy": "2.1.0",
"express": "4.17.2",
"follow-redirects": "1.14.7",
"follow-redirects": "1.14.8",
"graphql": "15.7.1",
"graphql-list-fields": "2.0.2",
"graphql-relay": "0.7.0",
@@ -40,14 +41,14 @@
"graphql-upload": "11.0.0",
"intersect": "1.0.1",
"jsonwebtoken": "8.5.1",
"jwks-rsa": "1.12.3",
"jwks-rsa": "2.0.5",
"ldapjs": "2.3.1",
"lodash": "4.17.21",
"lru-cache": "5.1.1",
"lru-cache": "6.0.0",
"mime": "3.0.0",
"mongodb": "3.6.11",
"mongodb": "4.3.1",
"mustache": "4.2.0",
"parse": "3.4.0",
"parse": "3.4.1",
"pg-monitor": "1.4.1",
"pg-promise": "10.11.1",
"pluralize": "8.0.0",
@@ -56,19 +57,19 @@
"subscriptions-transport-ws": "0.11.0",
"tv4": "1.3.0",
"uuid": "8.3.2",
"winston": "3.3.3",
"winston-daily-rotate-file": "4.5.5",
"winston": "3.5.1",
"winston-daily-rotate-file": "4.6.0",
"ws": "8.2.3"
},
"devDependencies": {
"@actions/core": "1.2.6",
"@apollo/client": "3.4.17",
"@babel/cli": "7.10.0",
"@babel/core": "7.10.0",
"@babel/plugin-proposal-object-rest-spread": "7.10.0",
"@babel/plugin-transform-flow-strip-types": "7.9.0",
"@babel/preset-env": "7.10.0",
"@parse/minami": "1.0.0",
"@saithodev/semantic-release-backmerge": "2.1.2",
"@semantic-release/changelog": "5.0.1",
"@semantic-release/commit-analyzer": "8.0.1",
"@semantic-release/git": "9.0.0",
@@ -118,12 +119,13 @@
"test:mongodb:4.0.27": "npm run test:mongodb --dbversion=4.0.27",
"test:mongodb:4.2.17": "npm run test:mongodb --dbversion=4.2.17",
"test:mongodb:4.4.10": "npm run test:mongodb --dbversion=4.4.10",
"test:mongodb:5.0.5": "npm run test:mongodb --dbversion=5.0.5",
"posttest:mongodb": "mongodb-runner stop",
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.4.10} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start",
"testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.4.10} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine",
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start",
"testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine",
"test": "npm run testonly",
"posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.4.10} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop",
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.4.10} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine",
"posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop",
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine",
"start": "node ./bin/parse-server",
"prettier": "prettier --write {src,spec}/{**/*,*}.js",
"prepare": "npm run build",
@@ -131,7 +133,7 @@
"madge:circular": "node_modules/.bin/madge ./src --circular"
},
"engines": {
"node": ">=12.20.0 <17"
"node": ">=12.22.10 <17"
},
"bin": {
"parse-server": "bin/parse-server"
+9
View File
@@ -88,6 +88,15 @@ async function config() {
labels: ['type:ci'],
releasedLabels: ['state:released<%= nextRelease.channel ? `-\${nextRelease.channel}` : "" %>']
}],
[
"@saithodev/semantic-release-backmerge",
{
"branches": [
{ from: "beta", to: "alpha" },
{ from: "release", to: "beta" },
]
}
],
],
};
+14
View File
@@ -172,6 +172,20 @@ function parseDefaultValue(elt, value, t) {
literalValue = t.arrayExpression(array.map((value) => {
if (typeof value == 'string') {
return t.stringLiteral(value);
} else if (typeof value == 'number') {
return t.numericLiteral(value);
} else if (typeof value == 'object') {
const object = parsers.objectParser(value);
const props = Object.entries(object).map(([k, v]) => {
if (typeof v == 'string') {
return t.objectProperty(t.identifier(k), t.stringLiteral(v));
} else if (typeof v == 'number') {
return t.objectProperty(t.identifier(k), t.numericLiteral(v));
} else if (typeof v == 'boolean') {
return t.objectProperty(t.identifier(k), t.booleanLiteral(v));
}
});
return t.objectExpression(props);
} else {
throw new Error('Unable to parse array');
}
+15 -14
View File
@@ -326,22 +326,24 @@ describe('AudiencesRouter', () => {
{ name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) },
{ useMasterKey: true }
).then(audience => {
database.collection('test__Audience').updateOne(
{ _id: audience.objectId },
{
$set: {
times_used: 1,
_last_used: now,
},
},
{},
error => {
expect(error).toEqual(null);
database
.collection('test__Audience')
.updateOne(
{ _id: audience.objectId },
{
$set: {
times_used: 1,
_last_used: now,
},
}
)
.then(result => {
expect(result).toBeTruthy();
database
.collection('test__Audience')
.find({ _id: audience.objectId })
.toArray((error, rows) => {
expect(error).toEqual(null);
expect(error).toEqual(undefined);
expect(rows[0]['times_used']).toEqual(1);
expect(rows[0]['_last_used']).toEqual(now);
Parse._request(
@@ -361,8 +363,7 @@ describe('AudiencesRouter', () => {
done.fail(error);
});
});
}
);
});
});
});
+161 -47
View File
@@ -1652,8 +1652,41 @@ describe('apple signin auth adapter', () => {
describe('Apple Game Center Auth adapter', () => {
const gcenter = require('../lib/Adapters/Auth/gcenter');
const fs = require('fs');
const testCert = fs.readFileSync(__dirname + '/support/cert/game_center.pem');
it('can load adapter', async () => {
const options = {
gcenter: {
rootCertificateUrl:
'https://cacerts.digicert.com/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem',
},
};
const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
'gcenter',
options
);
await adapter.validateAppId(
appIds,
{ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer' },
providerOptions
);
});
it('validateAuthData should validate', async () => {
const options = {
gcenter: {
rootCertificateUrl:
'https://cacerts.digicert.com/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem',
},
};
const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
'gcenter',
options
);
await adapter.validateAppId(
appIds,
{ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer' },
providerOptions
);
// real token is used
const authData = {
id: 'G:1965586982',
@@ -1664,66 +1697,147 @@ describe('Apple Game Center Auth adapter', () => {
salt: 'DzqqrQ==',
bundleId: 'cloud.xtralife.gamecenterauth',
};
try {
await gcenter.validateAuthData(authData);
} catch (e) {
fail();
}
gcenter.cache['https://static.gc.apple.com/public-key/gc-prod-4.cer'] = testCert;
await gcenter.validateAuthData(authData);
});
it('validateAuthData invalid signature id', async () => {
const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
'gcenter',
{}
);
await adapter.validateAppId(
appIds,
{ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer' },
providerOptions
);
const authData = {
id: 'G:1965586982',
publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-6.cer',
timestamp: 1565257031287,
signature: '1234',
salt: 'DzqqrQ==',
bundleId: 'cloud.xtralife.gamecenterauth',
bundleId: 'com.example.com',
};
try {
await gcenter.validateAuthData(authData);
fail();
} catch (e) {
expect(e.message).toBe('Apple Game Center - invalid signature');
}
});
it('validateAuthData invalid public key url', async () => {
const authData = {
id: 'G:1965586982',
publicKeyUrl: 'invalid.com',
timestamp: 1565257031287,
signature: '1234',
salt: 'DzqqrQ==',
bundleId: 'cloud.xtralife.gamecenterauth',
};
try {
await gcenter.validateAuthData(authData);
fail();
} catch (e) {
expect(e.message).toBe('Apple Game Center - invalid publicKeyUrl: invalid.com');
}
await expectAsync(gcenter.validateAuthData(authData)).toBeRejectedWith(
new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Apple Game Center - invalid signature')
);
});
it('validateAuthData invalid public key http url', async () => {
const authData = {
id: 'G:1965586982',
publicKeyUrl: 'http://static.gc.apple.com/public-key/gc-prod-4.cer',
timestamp: 1565257031287,
signature: '1234',
salt: 'DzqqrQ==',
bundleId: 'cloud.xtralife.gamecenterauth',
const options = {
gcenter: {
rootCertificateUrl:
'https://cacerts.digicert.com/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem',
},
};
const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
'gcenter',
options
);
await adapter.validateAppId(
appIds,
{ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer' },
providerOptions
);
const publicKeyUrls = [
'example.com',
'http://static.gc.apple.com/public-key/gc-prod-4.cer',
'https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg',
'https://example.com/ \\.apple.com/public_key.cer',
'https://example.com/ &.apple.com/public_key.cer',
];
await Promise.all(
publicKeyUrls.map(publicKeyUrl =>
expectAsync(
gcenter.validateAuthData({
id: 'G:1965586982',
timestamp: 1565257031287,
publicKeyUrl,
signature: '1234',
salt: 'DzqqrQ==',
bundleId: 'com.example.com',
})
).toBeRejectedWith(
new Parse.Error(
Parse.Error.SCRIPT_FAILED,
`Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`
)
)
)
);
});
try {
await gcenter.validateAuthData(authData);
fail();
} catch (e) {
expect(e.message).toBe('Apple Game Center - invalid publicKeyUrl: http://static.gc.apple.com/public-key/gc-prod-4.cer');
}
it('should not validate Symantec Cert', async () => {
const options = {
gcenter: {
rootCertificateUrl:
'https://cacerts.digicert.com/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem',
},
};
const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
'gcenter',
options
);
await adapter.validateAppId(
appIds,
{ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer' },
providerOptions
);
expect(() =>
gcenter.verifyPublicKeyIssuer(
testCert,
'https://static.gc.apple.com/public-key/gc-prod-4.cer'
)
);
});
it('adapter should load default cert', async () => {
const options = {
gcenter: {},
};
const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
'gcenter',
options
);
await adapter.validateAppId(
appIds,
{ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer' },
providerOptions
);
const previous = new Date();
await adapter.validateAppId(
appIds,
{ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer' },
providerOptions
);
const duration = new Date().getTime() - previous.getTime();
expect(duration).toEqual(0);
});
it('adapter should throw', async () => {
const options = {
gcenter: {
rootCertificateUrl: 'https://example.com',
},
};
const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
'gcenter',
options
);
await expectAsync(
adapter.validateAppId(
appIds,
{ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer' },
providerOptions
)
).toBeRejectedWith(
new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Apple Game Center auth adapter parameter `rootCertificateURL` is invalid.'
)
);
});
});
+15
View File
@@ -3224,6 +3224,21 @@ describe('afterLogin hook', () => {
const query = new Parse.Query(TestObject);
await query.find({ context: { a: 'a' } });
});
it('beforeFind and afterFind should have access to context while making fetch call', async () => {
Parse.Cloud.beforeFind('TestObject', req => {
expect(req.context.a).toEqual('a');
expect(req.context.b).toBeUndefined();
req.context.b = 'b';
});
Parse.Cloud.afterFind('TestObject', req => {
expect(req.context.a).toEqual('a');
expect(req.context.b).toEqual('b');
});
const obj = new TestObject();
await obj.save();
await obj.fetch({ context: { a: 'a' } });
});
});
describe('saveFile hooks', () => {
+21 -22
View File
@@ -3,7 +3,6 @@ const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapte
.WinstonLoggerAdapter;
const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
.GridFSBucketAdapter;
const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter').GridStoreAdapter;
const Config = require('../lib/Config');
const FilesController = require('../lib/Controllers/FilesController').default;
const databaseURI = 'mongodb://localhost:27017/parse';
@@ -24,8 +23,8 @@ const mockAdapter = {
describe('FilesController', () => {
it('should properly expand objects', done => {
const config = Config.get(Parse.applicationId);
const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
const filesController = new FilesController(gridStoreAdapter);
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
const filesController = new FilesController(gridFSAdapter);
const result = filesController.expandFilesInObject(config, function () {});
expect(result).toBeUndefined();
@@ -88,19 +87,19 @@ describe('FilesController', () => {
it('should add a unique hash to the file name when the preserveFileName option is false', done => {
const config = Config.get(Parse.applicationId);
const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
spyOn(gridStoreAdapter, 'createFile');
gridStoreAdapter.createFile.and.returnValue(Promise.resolve());
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
spyOn(gridFSAdapter, 'createFile');
gridFSAdapter.createFile.and.returnValue(Promise.resolve());
const fileName = 'randomFileName.pdf';
const regexEscapedFileName = fileName.replace(/\./g, '\\$&');
const filesController = new FilesController(gridStoreAdapter, null, {
const filesController = new FilesController(gridFSAdapter, null, {
preserveFileName: false,
});
filesController.createFile(config, fileName);
expect(gridStoreAdapter.createFile).toHaveBeenCalledTimes(1);
expect(gridStoreAdapter.createFile.calls.mostRecent().args[0]).toMatch(
expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toMatch(
`^.{32}_${regexEscapedFileName}$`
);
@@ -109,42 +108,42 @@ describe('FilesController', () => {
it('should not add a unique hash to the file name when the preserveFileName option is true', done => {
const config = Config.get(Parse.applicationId);
const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
spyOn(gridStoreAdapter, 'createFile');
gridStoreAdapter.createFile.and.returnValue(Promise.resolve());
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
spyOn(gridFSAdapter, 'createFile');
gridFSAdapter.createFile.and.returnValue(Promise.resolve());
const fileName = 'randomFileName.pdf';
const filesController = new FilesController(gridStoreAdapter, null, {
const filesController = new FilesController(gridFSAdapter, null, {
preserveFileName: true,
});
filesController.createFile(config, fileName);
expect(gridStoreAdapter.createFile).toHaveBeenCalledTimes(1);
expect(gridStoreAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName);
expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName);
done();
});
it('should handle adapter without getMetadata', async () => {
const gridStoreAdapter = new GridFSBucketAdapter(databaseURI);
gridStoreAdapter.getMetadata = null;
const filesController = new FilesController(gridStoreAdapter);
const gridFSAdapter = new GridFSBucketAdapter(databaseURI);
gridFSAdapter.getMetadata = null;
const filesController = new FilesController(gridFSAdapter);
const result = await filesController.getMetadata();
expect(result).toEqual({});
});
it('should reject slashes in file names', done => {
const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
const fileName = 'foo/randomFileName.pdf';
expect(gridStoreAdapter.validateFilename(fileName)).not.toBe(null);
expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null);
done();
});
it('should also reject slashes in file names', done => {
const gridStoreAdapter = new GridStoreAdapter('mongodb://localhost:27017/parse');
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
const fileName = 'foo/randomFileName.pdf';
expect(gridStoreAdapter.validateFilename(fileName)).not.toBe(null);
expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null);
done();
});
});
+3 -16
View File
@@ -1,4 +1,3 @@
const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter').GridStoreAdapter;
const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
.GridFSBucketAdapter;
const { randomString } = require('../lib/cryptoUtils');
@@ -14,25 +13,13 @@ async function expectMissingFile(gfsAdapter, name) {
}
}
describe_only_db('mongo')('GridFSBucket and GridStore interop', () => {
describe_only_db('mongo')('GridFSBucket', () => {
beforeEach(async () => {
const gsAdapter = new GridStoreAdapter(databaseURI);
const gsAdapter = new GridFSBucketAdapter(databaseURI);
const db = await gsAdapter._connect();
await db.dropDatabase();
});
it('a file created in GridStore should be available in GridFS', async () => {
const gsAdapter = new GridStoreAdapter(databaseURI);
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
await expectMissingFile(gfsAdapter, 'myFileName');
const originalString = 'abcdefghi';
await gsAdapter.createFile('myFileName', originalString);
const gsResult = await gsAdapter.getFileData('myFileName');
expect(gsResult.toString('utf8')).toBe(originalString);
const gfsResult = await gfsAdapter.getFileData('myFileName');
expect(gfsResult.toString('utf8')).toBe(originalString);
});
it('should save an encrypted file that can only be decrypted by a GridFS adapter with the encryptionKey', async () => {
const unencryptedAdapter = new GridFSBucketAdapter(databaseURI);
const encryptedAdapter = new GridFSBucketAdapter(
@@ -451,7 +438,7 @@ describe_only_db('mongo')('GridFSBucket and GridStore interop', () => {
await db.admin().serverStatus();
expect(false).toBe(true);
} catch (e) {
expect(e.message).toEqual('topology was destroyed');
expect(e.message).toEqual('MongoClient must be connected to perform this operation');
}
});
});
-111
View File
@@ -1,111 +0,0 @@
const MongoClient = require('mongodb').MongoClient;
const GridStore = require('mongodb').GridStore;
const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter').GridStoreAdapter;
const Config = require('../lib/Config');
const FilesController = require('../lib/Controllers/FilesController').default;
// Small additional tests to improve overall coverage
describe_only_db('mongo')('GridStoreAdapter', () => {
it('should properly instanciate the GridStore when deleting a file', async done => {
const databaseURI = 'mongodb://localhost:27017/parse';
const config = Config.get(Parse.applicationId);
const gridStoreAdapter = new GridStoreAdapter(databaseURI);
const db = await gridStoreAdapter._connect();
await db.dropDatabase();
const filesController = new FilesController(gridStoreAdapter, Parse.applicationId, {});
// save original unlink before redefinition
const originalUnlink = GridStore.prototype.unlink;
let gridStoreMode;
// new unlink method that will capture the mode in which GridStore was opened
GridStore.prototype.unlink = function () {
// restore original unlink during first call
GridStore.prototype.unlink = originalUnlink;
gridStoreMode = this.mode;
return originalUnlink.call(this);
};
filesController
.createFile(config, 'myFilename.txt', 'my file content', 'text/plain')
.then(myFile => {
return MongoClient.connect(databaseURI)
.then(client => {
const database = client.db(client.s.options.dbName);
// Verify the existance of the fs.files document
return database
.collection('fs.files')
.count()
.then(count => {
expect(count).toEqual(1);
return { database, client };
});
})
.then(({ database, client }) => {
// Verify the existance of the fs.files document
return database
.collection('fs.chunks')
.count()
.then(count => {
expect(count).toEqual(1);
return client.close();
});
})
.then(() => {
return filesController.deleteFile(config, myFile.name);
});
})
.then(() => {
return MongoClient.connect(databaseURI)
.then(client => {
const database = client.db(client.s.options.dbName);
// Verify the existance of the fs.files document
return database
.collection('fs.files')
.count()
.then(count => {
expect(count).toEqual(0);
return { database, client };
});
})
.then(({ database, client }) => {
// Verify the existance of the fs.files document
return database
.collection('fs.chunks')
.count()
.then(count => {
expect(count).toEqual(0);
return client.close();
});
});
})
.then(() => {
// Verify that gridStore was opened in read only mode
expect(gridStoreMode).toEqual('r');
done();
})
.catch(fail);
});
it('handleShutdown, close connection', async () => {
const databaseURI = 'mongodb://localhost:27017/parse';
const gridStoreAdapter = new GridStoreAdapter(databaseURI);
const db = await gridStoreAdapter._connect();
const status = await db.admin().serverStatus();
expect(status.connections.current > 0).toEqual(true);
await gridStoreAdapter.handleShutdown();
try {
await db.admin().serverStatus();
expect(false).toBe(true);
} catch (e) {
expect(e.message).toEqual('topology was destroyed');
}
});
});
+25 -22
View File
@@ -93,28 +93,31 @@ describe('Idempotency', () => {
expect(counter).toBe(2);
});
it_only_db('postgres')('should delete request entry when postgress ttl function is called', async () => {
const client = Config.get(Parse.applicationId).database.adapter._client;
let counter = 0;
Parse.Cloud.define('myFunction', () => {
counter++;
});
const params = {
method: 'POST',
url: 'http://localhost:8378/1/functions/myFunction',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Master-Key': Parse.masterKey,
'X-Parse-Request-Id': 'abc-123',
},
};
await expectAsync(request(params)).toBeResolved();
await expectAsync(request(params)).toBeRejected();
await new Promise(resolve => setTimeout(resolve, maxTimeOut));
await client.one('SELECT idempotency_delete_expired_records()');
await expectAsync(request(params)).toBeResolved();
expect(counter).toBe(2);
});
it_only_db('postgres')(
'should delete request entry when postgress ttl function is called',
async () => {
const client = Config.get(Parse.applicationId).database.adapter._client;
let counter = 0;
Parse.Cloud.define('myFunction', () => {
counter++;
});
const params = {
method: 'POST',
url: 'http://localhost:8378/1/functions/myFunction',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Master-Key': Parse.masterKey,
'X-Parse-Request-Id': 'abc-123',
},
};
await expectAsync(request(params)).toBeResolved();
await expectAsync(request(params)).toBeRejected();
await new Promise(resolve => setTimeout(resolve, maxTimeOut));
await client.one('SELECT idempotency_delete_expired_records()');
await expectAsync(request(params)).toBeResolved();
expect(counter).toBe(2);
}
);
it('should enforce idempotency for cloud code jobs', async () => {
let counter = 0;
+24 -31
View File
@@ -1,7 +1,7 @@
'use strict';
const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default;
const { MongoClient } = require('mongodb');
const { MongoClient, Collection } = require('mongodb');
const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
const request = require('../lib/request');
const Config = require('../lib/Config');
@@ -101,7 +101,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
done.fail('Find succeeded despite taking too long!');
},
err => {
expect(err.name).toEqual('MongoError');
expect(err.name).toEqual('MongoServerError');
expect(err.code).toEqual(50);
expect(err.message).toMatch('operation exceeded time limit');
done();
@@ -283,7 +283,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
await adapter.database.admin().serverStatus();
expect(false).toBe(true);
} catch (e) {
expect(e.message).toEqual('topology was destroyed');
expect(e.message).toEqual('MongoClient must be connected to perform this operation');
}
});
@@ -392,8 +392,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
const myObject = new Parse.Object('MyObject');
await myObject.save();
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough();
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
await request({
method: 'POST',
@@ -412,9 +411,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
});
let found = false;
databaseAdapter.database.serverConfig.command.calls.all().forEach(call => {
Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
found = true;
expect(call.args[2].session.transaction.state).not.toBe('NO_TRANSACTION');
expect(call.args[2].session.transaction.state).toBe('TRANSACTION_COMMITTED');
});
expect(found).toBe(true);
});
@@ -423,8 +422,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
const myObject = new Parse.Object('MyObject');
await myObject.save();
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough();
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
await request({
method: 'POST',
@@ -443,9 +441,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
});
let found = false;
databaseAdapter.database.serverConfig.command.calls.all().forEach(call => {
Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
found = true;
expect(call.args[2].session).toBe(undefined);
expect(call.args[2].session).toBeFalsy();
});
expect(found).toBe(true);
});
@@ -454,8 +452,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
const myObject = new Parse.Object('MyObject');
await myObject.save();
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough();
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
await request({
method: 'POST',
@@ -473,9 +470,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
});
let found = false;
databaseAdapter.database.serverConfig.command.calls.all().forEach(call => {
Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
found = true;
expect(call.args[2].session).toBe(undefined);
expect(call.args[2].session).toBeFalsy();
});
expect(found).toBe(true);
});
@@ -484,8 +481,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
const myObject = new Parse.Object('MyObject');
await myObject.save();
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough();
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
await request({
method: 'PUT',
@@ -495,30 +491,28 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
});
let found = false;
databaseAdapter.database.serverConfig.command.calls.all().forEach(call => {
Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
found = true;
expect(call.args[2].session).toBe(undefined);
expect(call.args[2].session).toBeFalsy();
});
expect(found).toBe(true);
});
it('should not use transactions when using SDK insert', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'insert').and.callThrough();
spyOn(Collection.prototype, 'insertOne').and.callThrough();
const myObject = new Parse.Object('MyObject');
await myObject.save();
const calls = databaseAdapter.database.serverConfig.insert.calls.all();
const calls = Collection.prototype.insertOne.calls.all();
expect(calls.length).toBeGreaterThan(0);
calls.forEach(call => {
expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION');
expect(call.args[1].session).toBeFalsy();
});
});
it('should not use transactions when using SDK update', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'update').and.callThrough();
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
const myObject = new Parse.Object('MyObject');
await myObject.save();
@@ -526,26 +520,25 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
myObject.set('myAttribute', 'myValue');
await myObject.save();
const calls = databaseAdapter.database.serverConfig.update.calls.all();
const calls = Collection.prototype.findOneAndUpdate.calls.all();
expect(calls.length).toBeGreaterThan(0);
calls.forEach(call => {
expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION');
expect(call.args[2].session).toBeFalsy();
});
});
it('should not use transactions when using SDK delete', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'remove').and.callThrough();
spyOn(Collection.prototype, 'deleteMany').and.callThrough();
const myObject = new Parse.Object('MyObject');
await myObject.save();
await myObject.destroy();
const calls = databaseAdapter.database.serverConfig.remove.calls.all();
const calls = Collection.prototype.deleteMany.calls.all();
expect(calls.length).toBeGreaterThan(0);
calls.forEach(call => {
expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION');
expect(call.args[1].session).toBeFalsy();
});
});
});
+43 -50
View File
@@ -32,7 +32,7 @@ const {
} = require('graphql');
const { ParseServer } = require('../');
const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer');
const ReadPreference = require('mongodb').ReadPreference;
const { ReadPreference, Collection } = require('mongodb');
const { v4: uuidv4 } = require('uuid');
function handleError(e) {
@@ -4473,8 +4473,7 @@ describe('ParseGraphQLServer', () => {
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
const databaseAdapter = parseServer.config.databaseController.adapter;
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await apolloClient.query({
query: gql`
@@ -4498,13 +4497,13 @@ describe('ParseGraphQLServer', () => {
let foundGraphQLClassReadPreference = false;
let foundUserClassReadPreference = false;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
foundGraphQLClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
foundUserClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
}
});
@@ -4520,8 +4519,7 @@ describe('ParseGraphQLServer', () => {
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
const databaseAdapter = parseServer.config.databaseController.adapter;
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await apolloClient.query({
query: gql`
@@ -4545,13 +4543,13 @@ describe('ParseGraphQLServer', () => {
let foundGraphQLClassReadPreference = false;
let foundUserClassReadPreference = false;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
foundGraphQLClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
foundUserClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
}
});
@@ -4564,8 +4562,7 @@ describe('ParseGraphQLServer', () => {
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
const databaseAdapter = parseServer.config.databaseController.adapter;
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await apolloClient.query({
query: gql`
@@ -4592,13 +4589,13 @@ describe('ParseGraphQLServer', () => {
let foundGraphQLClassReadPreference = false;
let foundUserClassReadPreference = false;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
foundGraphQLClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
foundUserClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST);
expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST);
}
});
@@ -5456,8 +5453,7 @@ describe('ParseGraphQLServer', () => {
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
const databaseAdapter = parseServer.config.databaseController.adapter;
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await apolloClient.query({
query: gql`
@@ -5482,13 +5478,13 @@ describe('ParseGraphQLServer', () => {
let foundGraphQLClassReadPreference = false;
let foundUserClassReadPreference = false;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
foundGraphQLClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
foundUserClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
}
});
@@ -5501,8 +5497,7 @@ describe('ParseGraphQLServer', () => {
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
const databaseAdapter = parseServer.config.databaseController.adapter;
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await apolloClient.query({
query: gql`
@@ -5527,13 +5522,13 @@ describe('ParseGraphQLServer', () => {
let foundGraphQLClassReadPreference = false;
let foundUserClassReadPreference = false;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
foundGraphQLClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
foundUserClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
}
});
@@ -5546,8 +5541,7 @@ describe('ParseGraphQLServer', () => {
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
const databaseAdapter = parseServer.config.databaseController.adapter;
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await apolloClient.query({
query: gql`
@@ -5574,13 +5568,13 @@ describe('ParseGraphQLServer', () => {
let foundGraphQLClassReadPreference = false;
let foundUserClassReadPreference = false;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
foundGraphQLClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
foundUserClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST);
expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST);
}
});
@@ -5594,8 +5588,7 @@ describe('ParseGraphQLServer', () => {
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
const databaseAdapter = parseServer.config.databaseController.adapter;
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await apolloClient.query({
query: gql`
@@ -5632,13 +5625,13 @@ describe('ParseGraphQLServer', () => {
let foundGraphQLClassReadPreference = false;
let foundUserClassReadPreference = false;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
foundGraphQLClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
foundUserClassReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST);
expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST);
}
});
+90
View File
@@ -319,6 +319,41 @@ describe('ParseLiveQuery', function () {
await object.save();
});
it('can log on afterLiveQueryEvent throw', async () => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
});
const object = new TestObject();
await object.save();
const logger = require('../lib/logger').logger;
spyOn(logger, 'error').and.callFake(() => {});
let session = undefined;
Parse.Cloud.afterLiveQueryEvent('TestObject', ({ sessionToken }) => {
session = sessionToken;
/* eslint-disable no-undef */
foo.bar();
/* eslint-enable no-undef */
});
const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
const subscription = await query.subscribe();
object.set({ foo: 'bar' });
await object.save();
await new Promise(resolve => subscription.on('error', resolve));
expect(logger.error).toHaveBeenCalledWith(
`Failed running afterLiveQueryEvent on class TestObject for event update with session ${session} with:\n Error: {"message":"foo is not defined","code":141}`
);
});
it('can handle afterEvent sendEvent to false', async done => {
await reconfigureServer({
liveQuery: {
@@ -566,6 +601,33 @@ describe('ParseLiveQuery', function () {
await query.subscribe();
});
it('can log on beforeConnect throw', async () => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
});
const logger = require('../lib/logger').logger;
spyOn(logger, 'error').and.callFake(() => {});
let token = undefined;
Parse.Cloud.beforeConnect(({ sessionToken }) => {
token = sessionToken;
/* eslint-disable no-undef */
foo.bar();
/* eslint-enable no-undef */
});
new Parse.Query(TestObject).subscribe();
await new Promise(resolve => Parse.LiveQuery.on('error', resolve));
Parse.LiveQuery.removeAllListeners('error');
expect(logger.error).toHaveBeenCalledWith(
`Failed running beforeConnect for session ${token} with:\n Error: {"message":"foo is not defined","code":141}`
);
});
it('can handle beforeSubscribe error', async done => {
await reconfigureServer({
liveQuery: {
@@ -594,6 +656,34 @@ describe('ParseLiveQuery', function () {
});
});
it('can log on beforeSubscribe error', async () => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
});
const logger = require('../lib/logger').logger;
spyOn(logger, 'error').and.callFake(() => {});
Parse.Cloud.beforeSubscribe(TestObject, () => {
/* eslint-disable no-undef */
foo.bar();
/* eslint-enable no-undef */
});
const query = new Parse.Query(TestObject);
const subscription = await query.subscribe();
await new Promise(resolve => subscription.on('error', resolve));
expect(logger.error).toHaveBeenCalledWith(
`Failed running beforeSubscribe on TestObject for session undefined with:\n Error: {"message":"foo is not defined","code":141}`
);
});
it('can handle mutate beforeSubscribe query', async done => {
await reconfigureServer({
liveQuery: {
+5 -5
View File
@@ -4777,7 +4777,7 @@ describe('Parse.Query testing', () => {
ttl: new Date(now - 2 * 24 * 60 * 60 * 1000), // 2 days ago
});
await Parse.Object.saveAll([obj1, obj2])
await Parse.Object.saveAll([obj1, obj2]);
const q1 = new Parse.Query('MyCustomObject');
q1.greaterThan('ttl', { $relativeTime: 'in 1 day' });
const results1 = await q1.find({ useMasterKey: true });
@@ -4825,8 +4825,8 @@ describe('Parse.Query testing', () => {
q.greaterThan('ttl', { $relativeTime: '-12 bananas ago' });
try {
await q.find({ useMasterKey: true });
fail("Should have thrown error");
} catch(error) {
fail('Should have thrown error');
} catch (error) {
expect(error.code).toBe(Parse.Error.INVALID_JSON);
}
});
@@ -4842,8 +4842,8 @@ describe('Parse.Query testing', () => {
q.greaterThan('nonDateField', { $relativeTime: '1 day ago' });
try {
await q.find({ useMasterKey: true });
fail("Should have thrown error");
} catch(error) {
fail('Should have thrown error');
} catch (error) {
expect(error.code).toBe(Parse.Error.INVALID_JSON);
}
});
+1 -4
View File
@@ -186,10 +186,7 @@ describe('ParseServerRESTController', () => {
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
);
}
expect(results.map(result => result.get('key')).sort()).toEqual([
'value1',
'value2',
]);
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
});
it('should not save anything when one operation fails in a transaction', async () => {
+2 -3
View File
@@ -24,7 +24,6 @@ const GameScore = Parse.Object.extend({
});
describe_only_db('postgres')('Postgres database init options', () => {
it('should create server with public schema databaseOptions', async () => {
const adapter = new PostgresStorageAdapter({
uri: postgresURI,
@@ -71,8 +70,8 @@ describe_only_db('postgres')('Postgres database init options', () => {
await reconfigureServer({
databaseAdapter: adapter,
});
fail("Should have thrown error");
} catch(error) {
fail('Should have thrown error');
} catch (error) {
expect(error).toBeDefined();
}
});
+21 -20
View File
@@ -179,14 +179,14 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => {
{
createdAt: {
$eq: {
$relativeTime: '12 days ago'
}
}
$relativeTime: '12 days ago',
},
},
},
{ }
{}
);
fail("Should have thrown error");
} catch(error) {
fail('Should have thrown error');
} catch (error) {
expect(error.code).toBe(Parse.Error.INVALID_JSON);
}
await dropTable(client, tableName);
@@ -222,14 +222,14 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => {
{
createdAt: {
$ne: {
$relativeTime: '12 days ago'
}
}
$relativeTime: '12 days ago',
},
},
},
{ }
{}
);
fail("Should have thrown error");
} catch(error) {
fail('Should have thrown error');
} catch (error) {
expect(error.code).toBe(Parse.Error.INVALID_JSON);
}
await dropTable(client, tableName);
@@ -265,14 +265,14 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => {
{
createdAt: {
$exists: {
$relativeTime: '12 days ago'
}
}
$relativeTime: '12 days ago',
},
},
},
{ }
{}
);
fail("Should have thrown error");
} catch(error) {
fail('Should have thrown error');
} catch (error) {
expect(error.code).toBe(Parse.Error.INVALID_JSON);
}
await dropTable(client, tableName);
@@ -563,9 +563,10 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => {
await reconfigureServer();
const adapter = Config.get('test').database.adapter;
const client = adapter._client;
const qs = "SELECT format('%I.%I(%s)', ns.nspname, p.proname, oidvectortypes(p.proargtypes)) FROM pg_proc p INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid) WHERE p.proname = 'idempotency_delete_expired_records'";
const qs =
"SELECT format('%I.%I(%s)', ns.nspname, p.proname, oidvectortypes(p.proargtypes)) FROM pg_proc p INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid) WHERE p.proname = 'idempotency_delete_expired_records'";
const foundFunction = await client.one(qs);
expect(foundFunction.format).toBe("public.idempotency_delete_expired_records()");
expect(foundFunction.format).toBe('public.idempotency_delete_expired_records()');
await adapter.deleteIdempotencyFunction();
await client.none(qs);
});
+157 -215
View File
@@ -1,9 +1,8 @@
'use strict';
const Parse = require('parse/node');
const ReadPreference = require('mongodb').ReadPreference;
const { ReadPreference, Collection } = require('mongodb');
const request = require('../lib/request');
const Config = require('../lib/Config');
function waitForReplication() {
return new Promise(function (resolve) {
@@ -13,8 +12,6 @@ function waitForReplication() {
describe_only_db('mongo')('Read preference option', () => {
it('should find in primary by default', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
@@ -22,7 +19,7 @@ describe_only_db('mongo')('Read preference option', () => {
Parse.Object.saveAll([obj0, obj1])
.then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
@@ -31,10 +28,10 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
}
});
@@ -58,15 +55,13 @@ describe_only_db('mongo')('Read preference option', () => {
databaseAdapter: new MongoStorageAdapter(adapterOptions),
});
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
@@ -76,10 +71,10 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results[0].get('boolKey')).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = true;
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST);
expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST);
}
});
@@ -87,15 +82,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference in the beforeFind trigger', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY';
@@ -110,9 +103,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results[0].get('boolKey')).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -120,15 +113,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should check read preference as case insensitive', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'sEcOnDarY';
@@ -144,9 +135,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results[0].get('boolKey')).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -154,15 +145,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference in the beforeFind trigger even changing query', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.query.equalTo('boolKey', true);
@@ -178,9 +167,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results[0].get('boolKey')).toBe(true);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -188,15 +177,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference in the beforeFind trigger even returning query', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY';
@@ -216,9 +203,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results[0].get('boolKey')).toBe(true);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -226,15 +213,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference in the beforeFind trigger even returning promise', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY';
@@ -253,9 +238,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results[0].get('boolKey')).toBe(true);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -263,15 +248,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference to PRIMARY_PREFERRED', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'PRIMARY_PREFERRED';
@@ -286,9 +269,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results[0].get('boolKey')).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -296,15 +279,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference to SECONDARY_PREFERRED', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY_PREFERRED';
@@ -319,9 +300,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results[0].get('boolKey')).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -329,15 +310,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference to NEAREST', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'NEAREST';
@@ -352,9 +331,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(results[0].get('boolKey')).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -362,15 +341,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference for GET', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY';
@@ -383,9 +360,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(result.get('boolKey')).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -393,15 +370,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference for GET using API', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY';
@@ -421,9 +396,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(body.boolKey).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -431,15 +406,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference for GET directly from API', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await waitForReplication();
const response = await request({
@@ -454,9 +427,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(response.data.boolKey).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -464,15 +437,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference for GET using API through the beforeFind overriding API option', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY_PREFERRED';
@@ -491,9 +462,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(response.data.boolKey).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -501,15 +472,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference for FIND using API through beforeFind trigger', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY';
@@ -528,9 +497,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(response.data.results.length).toEqual(2);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -538,15 +507,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference for FIND directly from API', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await waitForReplication();
const response = await request({
@@ -561,9 +528,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(response.data.results.length).toEqual(2);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -571,15 +538,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change read preference for FIND using API through the beforeFind overriding API option', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
await Parse.Object.saveAll([obj0, obj1]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY_PREFERRED';
@@ -598,9 +563,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(response.data.results.length).toEqual(2);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -608,15 +573,13 @@ describe_only_db('mongo')('Read preference option', () => {
});
xit('should change read preference for count', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
Parse.Object.saveAll([obj0, obj1]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY';
@@ -631,9 +594,9 @@ describe_only_db('mongo')('Read preference option', () => {
expect(result).toBe(1);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
@@ -659,17 +622,16 @@ describe_only_db('mongo')('Read preference option', () => {
await waitForReplication();
// Spy on DB adapter
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'startSession').and.callThrough();
spyOn(Collection.prototype, 'aggregate').and.callThrough();
// Query
const query = new Parse.Query('MyObject');
const results = await query.aggregate([{ match: { boolKey: false } }]);
// Validate
expect(results.length).toBe(1);
let readPreference = null;
databaseAdapter.database.serverConfig.startSession.calls.all().forEach(call => {
if (call.args[0].owner.ns.indexOf('MyObject') > -1) {
readPreference = call.args[0].owner.operation.readPreference.mode;
Collection.prototype.aggregate.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') > -1) {
readPreference = call.args[1].readPreference;
}
});
expect(readPreference).toEqual(ReadPreference.SECONDARY);
@@ -685,8 +647,7 @@ describe_only_db('mongo')('Read preference option', () => {
await waitForReplication();
// Spy on DB adapter
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
// Query
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
@@ -695,9 +656,9 @@ describe_only_db('mongo')('Read preference option', () => {
// Validate
expect(results.length).toBe(1);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[1].readPreference;
}
});
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
@@ -713,8 +674,7 @@ describe_only_db('mongo')('Read preference option', () => {
await waitForReplication();
// Spy on DB adapter
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
spyOn(databaseAdapter.database.serverConfig, 'startSession').and.callThrough();
spyOn(Collection.prototype, 'aggregate').and.callThrough();
// Query
const query = new Parse.Query('MyObject');
query.readPreference('SECONDARY');
@@ -722,17 +682,15 @@ describe_only_db('mongo')('Read preference option', () => {
// Validate
expect(results.length).toBe(1);
let readPreference = null;
databaseAdapter.database.serverConfig.startSession.calls.all().forEach(call => {
if (call.args[0].owner.ns.indexOf('MyObject') > -1) {
readPreference = call.args[0].owner.operation.readPreference.mode;
Collection.prototype.aggregate.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject') > -1) {
readPreference = call.args[1].readPreference;
}
});
expect(readPreference).toEqual(ReadPreference.SECONDARY);
});
it('should find includes in same replica of readPreference by default', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
@@ -743,7 +701,7 @@ describe_only_db('mongo')('Read preference option', () => {
obj2.set('myObject1', obj1);
await Parse.Object.saveAll([obj0, obj1, obj2]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject2', req => {
req.readPreference = 'SECONDARY';
@@ -765,15 +723,15 @@ describe_only_db('mongo')('Read preference option', () => {
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[1].readPreference;
}
});
@@ -783,8 +741,6 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change includes read preference', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
@@ -795,7 +751,7 @@ describe_only_db('mongo')('Read preference option', () => {
obj2.set('myObject1', obj1);
await Parse.Object.saveAll([obj0, obj1, obj2]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject2', req => {
req.readPreference = 'SECONDARY_PREFERRED';
@@ -818,15 +774,15 @@ describe_only_db('mongo')('Read preference option', () => {
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[1].readPreference;
}
});
@@ -836,8 +792,6 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change includes read preference when finding through API', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
@@ -848,7 +802,7 @@ describe_only_db('mongo')('Read preference option', () => {
obj2.set('myObject1', obj1);
await Parse.Object.saveAll([obj0, obj1, obj2]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await waitForReplication();
const response = await request({
@@ -873,15 +827,15 @@ describe_only_db('mongo')('Read preference option', () => {
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[1].readPreference;
}
});
@@ -891,8 +845,6 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change includes read preference when getting through API', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
@@ -903,7 +855,7 @@ describe_only_db('mongo')('Read preference option', () => {
obj2.set('myObject1', obj1);
await Parse.Object.saveAll([obj0, obj1, obj2]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await waitForReplication();
const response = await request({
@@ -929,15 +881,15 @@ describe_only_db('mongo')('Read preference option', () => {
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[1].readPreference;
}
});
@@ -947,8 +899,6 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should find subqueries in same replica of readPreference by default', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
@@ -959,7 +909,7 @@ describe_only_db('mongo')('Read preference option', () => {
obj2.set('myObject1', obj1);
await Parse.Object.saveAll([obj0, obj1, obj2]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject2', req => {
req.readPreference = 'SECONDARY';
@@ -982,15 +932,15 @@ describe_only_db('mongo')('Read preference option', () => {
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[1].readPreference;
}
});
@@ -1000,8 +950,6 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change subqueries read preference when using matchesQuery', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
@@ -1012,7 +960,7 @@ describe_only_db('mongo')('Read preference option', () => {
obj2.set('myObject1', obj1);
await Parse.Object.saveAll([obj0, obj1, obj2]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject2', req => {
req.readPreference = 'SECONDARY_PREFERRED';
@@ -1036,15 +984,15 @@ describe_only_db('mongo')('Read preference option', () => {
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[1].readPreference;
}
});
@@ -1054,8 +1002,6 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change subqueries read preference when using doesNotMatchQuery', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
@@ -1066,7 +1012,7 @@ describe_only_db('mongo')('Read preference option', () => {
obj2.set('myObject1', obj1);
await Parse.Object.saveAll([obj0, obj1, obj2]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject2', req => {
req.readPreference = 'SECONDARY_PREFERRED';
@@ -1090,15 +1036,15 @@ describe_only_db('mongo')('Read preference option', () => {
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[1].readPreference;
}
});
@@ -1108,8 +1054,6 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
@@ -1120,7 +1064,7 @@ describe_only_db('mongo')('Read preference option', () => {
obj2.set('myObject1', obj1);
await Parse.Object.saveAll([obj0, obj1, obj2]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
Parse.Cloud.beforeFind('MyObject2', req => {
req.readPreference = 'SECONDARY_PREFERRED';
@@ -1145,15 +1089,15 @@ describe_only_db('mongo')('Read preference option', () => {
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[1].readPreference;
}
});
@@ -1163,8 +1107,6 @@ describe_only_db('mongo')('Read preference option', () => {
});
it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery to find through API', async () => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
@@ -1175,7 +1117,7 @@ describe_only_db('mongo')('Read preference option', () => {
obj2.set('myObject1', obj1);
await Parse.Object.saveAll([obj0, obj1, obj2]);
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
spyOn(Collection.prototype, 'find').and.callThrough();
await waitForReplication();
const whereString = JSON.stringify({
@@ -1215,15 +1157,15 @@ describe_only_db('mongo')('Read preference option', () => {
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
Collection.prototype.find.calls.all().forEach(call => {
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[1].readPreference;
}
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[1].readPreference;
}
});
+1 -4
View File
@@ -267,10 +267,7 @@ describe('batch', () => {
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
);
}
expect(results.map(result => result.get('key')).sort()).toEqual([
'value1',
'value2',
]);
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
});
it('should not save anything when one operation fails in a transaction', async () => {
+28
View File
@@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEvDCCA6SgAwIBAgIQXRHxNXkw1L9z5/3EZ/T/hDANBgkqhkiG9w0BAQsFADB/
MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAd
BgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxMDAuBgNVBAMTJ1N5bWFudGVj
IENsYXNzIDMgU0hBMjU2IENvZGUgU2lnbmluZyBDQTAeFw0xODA5MTcwMDAwMDBa
Fw0xOTA5MTcyMzU5NTlaMHMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9y
bmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8xFDASBgNVBAoMC0FwcGxlLCBJbmMuMQ8w
DQYDVQQLDAZHQyBTUkUxFDASBgNVBAMMC0FwcGxlLCBJbmMuMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA06fwIi8fgKrTQu7cBcFkJVF6+Tqvkg7MKJTM
IOYPPQtPF3AZYPsbUoRKAD7/JXrxxOSVJ7vU1mP77tYG8TcUteZ3sAwvt2dkRbm7
ZO6DcmSggv1Dg4k3goNw4GYyCY4Z2/8JSmsQ80Iv/UOOwynpBziEeZmJ4uck6zlA
17cDkH48LBpKylaqthym5bFs9gj11pto7mvyb5BTcVuohwi6qosvbs/4VGbC2Nsz
ie416nUZfv+xxoXH995gxR2mw5cDdeCew7pSKxEhvYjT2nVdQF0q/hnPMFnOaEyT
q79n3gwFXyt0dy8eP6KBF7EW9J6b7ubu/j7h+tQfxPM+gTXOBQIDAQABo4IBPjCC
ATowCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUH
AwMwYQYDVR0gBFowWDBWBgZngQwBBAEwTDAjBggrBgEFBQcCARYXaHR0cHM6Ly9k
LnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGQwXaHR0cHM6Ly9kLnN5bWNiLmNv
bS9ycGEwHwYDVR0jBBgwFoAUljtT8Hkzl699g+8uK8zKt4YecmYwKwYDVR0fBCQw
IjAgoB6gHIYaaHR0cDovL3N2LnN5bWNiLmNvbS9zdi5jcmwwVwYIKwYBBQUHAQEE
SzBJMB8GCCsGAQUFBzABhhNodHRwOi8vc3Yuc3ltY2QuY29tMCYGCCsGAQUFBzAC
hhpodHRwOi8vc3Yuc3ltY2IuY29tL3N2LmNydDANBgkqhkiG9w0BAQsFAAOCAQEA
I/j/PcCNPebSAGrcqSFBSa2mmbusOX01eVBg8X0G/z8Z+ZWUfGFzDG0GQf89MPxV
woec+nZuqui7o9Bg8s8JbHV0TC52X14CbTj9w/qBF748WbH9gAaTkrJYPm+MlNhu
tjEuQdNl/YXVMvQW4O8UMHTi09GyJQ0NC4q92Wxvx1m/qzjvTLvrXHGQ9pEHhPyz
vfBLxQkWpNoCNKU7UeESyH06XOrGc9MsII9deeKsDJp9a0jtx+pP4MFVtFME9SSQ
tMBs0It7WwEf7qcRLpialxKwY2EzQ9g4WnANHqo18PrDBE10TFpZPzUh7JhMViVr
EEbl0YdElmF8Hlamah/yNw==
-----END CERTIFICATE-----
+297
View File
@@ -0,0 +1,297 @@
const request = require('../lib/request');
describe('Vulnerabilities', () => {
describe('Object prototype pollution', () => {
it('denies object prototype to be polluted with keyword "constructor"', async () => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const response = await request({
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/PP',
body: JSON.stringify({
obj: {
constructor: {
prototype: {
dummy: 0,
},
},
},
}),
}).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"key":"constructor"}.');
expect(Object.prototype.dummy).toBeUndefined();
});
it('denies object prototype to be polluted with keypath string "constructor"', async () => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const objResponse = await request({
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/PP',
body: JSON.stringify({
obj: {},
}),
}).catch(e => e);
const pollResponse = await request({
headers: headers,
method: 'PUT',
url: `http://localhost:8378/1/classes/PP/${objResponse.data.objectId}`,
body: JSON.stringify({
'obj.constructor.prototype.dummy': {
__op: 'Increment',
amount: 1,
},
}),
}).catch(e => e);
expect(Object.prototype.dummy).toBeUndefined();
expect(pollResponse.status).toBe(400);
const text = JSON.parse(pollResponse.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"key":"constructor"}.');
expect(Object.prototype.dummy).toBeUndefined();
});
it('denies object prototype to be polluted with keyword "__proto__"', async () => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const response = await request({
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/PP',
body: JSON.stringify({ 'obj.__proto__.dummy': 0 }),
}).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"key":"__proto__"}.');
expect(Object.prototype.dummy).toBeUndefined();
});
});
describe('Request denylist', () => {
it('denies BSON type code data in write request by default', async () => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
_bsontype: 'Code',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe(
'Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.'
);
});
it('allows BSON type code data in write request with custom denylist', async () => {
await reconfigureServer({
requestKeywordDenylist: [],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
_bsontype: 'Code',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(201);
const text = JSON.parse(response.text);
expect(text.objectId).toBeDefined();
});
it('denies write request with custom denylist of key/value', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe(
'Prohibited keyword in request data: {"key":"a[K]ey","value":"aValue[123]*"}.'
);
});
it('denies write request with custom denylist of nested key/value', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
nested: {
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe(
'Prohibited keyword in request data: {"key":"a[K]ey","value":"aValue[123]*"}.'
);
});
it('denies write request with custom denylist of key/value in array', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: [
{
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
],
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe(
'Prohibited keyword in request data: {"key":"a[K]ey","value":"aValue[123]*"}.'
);
});
it('denies write request with custom denylist of key', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'a[K]ey' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"key":"a[K]ey"}.');
});
it('denies write request with custom denylist of value', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ value: 'aValue[123]*' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"value":"aValue[123]*"}.');
});
});
describe('Ignore non-matches', () => {
it('ignores write request that contains only fraction of denied keyword', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'abc' }],
});
// Initially saving an object executes the keyword detection in RestWrite.js
const obj = new TestObject({ a: { b: { c: 0 } } });
await expectAsync(obj.save()).toBeResolved();
// Modifying a nested key executes the keyword detection in DatabaseController.js
obj.increment('a.b.c');
await expectAsync(obj.save()).toBeResolved();
});
});
});
+99 -28
View File
@@ -14,21 +14,15 @@ const authData = {
const { Parse } = require('parse/node');
const crypto = require('crypto');
const https = require('https');
const { pki } = require('node-forge');
const ca = { cert: null, url: null };
const cache = {}; // (publicKey -> cert) cache
function verifyPublicKeyUrl(publicKeyUrl) {
try {
const parsedUrl = new URL(publicKeyUrl);
if (parsedUrl.protocol !== 'https:') {
return false;
}
const hostnameParts = parsedUrl.hostname.split('.');
const length = hostnameParts.length;
const domainParts = hostnameParts.slice(length - 2, length);
const domain = domainParts.join('.');
return domain === 'apple.com';
} catch(error) {
const regex = /^https:\/\/(?:[-_A-Za-z0-9]+\.){0,}apple\.com\/.*\.cer$/;
return regex.test(publicKeyUrl);
} catch (error) {
return false;
}
}
@@ -43,7 +37,7 @@ function convertX509CertToPEM(X509Cert) {
return pemPreFix + certBody + pemPostFix;
}
function getAppleCertificate(publicKeyUrl) {
async function getAppleCertificate(publicKeyUrl) {
if (!verifyPublicKeyUrl(publicKeyUrl)) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
@@ -53,26 +47,59 @@ function getAppleCertificate(publicKeyUrl) {
if (cache[publicKeyUrl]) {
return cache[publicKeyUrl];
}
const url = new URL(publicKeyUrl);
const headOptions = {
hostname: url.hostname,
path: url.pathname,
method: 'HEAD',
};
const cert_headers = await new Promise((resolve, reject) =>
https.get(headOptions, res => resolve(res.headers)).on('error', reject)
);
const validContentTypes = ['application/x-x509-ca-cert', 'application/pkix-cert'];
if (
!validContentTypes.includes(cert_headers['content-type']) ||
cert_headers['content-length'] == null ||
cert_headers['content-length'] > 10000
) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
`Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`
);
}
const { certificate, headers } = await getCertificate(publicKeyUrl);
if (headers['cache-control']) {
const expire = headers['cache-control'].match(/max-age=([0-9]+)/);
if (expire) {
cache[publicKeyUrl] = certificate;
// we'll expire the cache entry later, as per max-age
setTimeout(() => {
delete cache[publicKeyUrl];
}, parseInt(expire[1], 10) * 1000);
}
}
return verifyPublicKeyIssuer(certificate, publicKeyUrl);
}
function getCertificate(url, buffer) {
return new Promise((resolve, reject) => {
https
.get(publicKeyUrl, res => {
let data = '';
.get(url, res => {
const data = [];
res.on('data', chunk => {
data += chunk.toString('base64');
data.push(chunk);
});
res.on('end', () => {
const cert = convertX509CertToPEM(data);
if (res.headers['cache-control']) {
var expire = res.headers['cache-control'].match(/max-age=([0-9]+)/);
if (expire) {
cache[publicKeyUrl] = cert;
// we'll expire the cache entry later, as per max-age
setTimeout(() => {
delete cache[publicKeyUrl];
}, parseInt(expire[1], 10) * 1000);
}
if (buffer) {
resolve({ certificate: Buffer.concat(data), headers: res.headers });
return;
}
resolve(cert);
let cert = '';
for (const chunk of data) {
cert += chunk.toString('base64');
}
const certificate = convertX509CertToPEM(cert);
resolve({ certificate, headers: res.headers });
});
})
.on('error', reject);
@@ -103,6 +130,30 @@ function verifySignature(publicKey, authData) {
}
}
function verifyPublicKeyIssuer(cert, publicKeyUrl) {
const publicKeyCert = pki.certificateFromPem(cert);
if (!ca.cert) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Apple Game Center auth adapter parameter `rootCertificateURL` is invalid.'
);
}
try {
if (!ca.cert.verify(publicKeyCert)) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
`Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`
);
}
} catch (e) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
`Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`
);
}
return cert;
}
// Returns a promise that fulfills if this user id is valid.
async function validateAuthData(authData) {
if (!authData.id) {
@@ -114,11 +165,31 @@ async function validateAuthData(authData) {
}
// Returns a promise that fulfills if this app id is valid.
function validateAppId() {
return Promise.resolve();
async function validateAppId(appIds, authData, options = {}) {
if (!options.rootCertificateUrl) {
options.rootCertificateUrl =
'https://cacerts.digicert.com/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem';
}
if (ca.url === options.rootCertificateUrl) {
return;
}
const { certificate, headers } = await getCertificate(options.rootCertificateUrl, true);
if (
headers['content-type'] !== 'application/x-pem-file' ||
headers['content-length'] == null ||
headers['content-length'] > 10000
) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Apple Game Center auth adapter parameter `rootCertificateURL` is invalid.'
);
}
ca.cert = pki.certificateFromPem(certificate);
ca.url = options.rootCertificateUrl;
}
module.exports = {
validateAppId,
validateAuthData,
cache,
};
+1 -1
View File
@@ -1,6 +1,6 @@
/**
GridFSBucketAdapter
Stores files in Mongo using GridStore
Stores files in Mongo using GridFS
Requires the database adapter to be based on mongoclient
@flow weak
+4 -181
View File
@@ -1,181 +1,4 @@
/**
GridStoreAdapter
Stores files in Mongo using GridStore
Requires the database adapter to be based on mongoclient
(GridStore is deprecated, Please use GridFSBucket instead)
@flow weak
*/
// @flow-disable-next
import { MongoClient, GridStore, Db } from 'mongodb';
import { FilesAdapter, validateFilename } from './FilesAdapter';
import defaults from '../../defaults';
export class GridStoreAdapter extends FilesAdapter {
_databaseURI: string;
_connectionPromise: Promise<Db>;
_mongoOptions: Object;
constructor(mongoDatabaseURI = defaults.DefaultMongoURI, mongoOptions = {}) {
super();
this._databaseURI = mongoDatabaseURI;
const defaultMongoOptions = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
this._mongoOptions = Object.assign(defaultMongoOptions, mongoOptions);
}
_connect() {
if (!this._connectionPromise) {
this._connectionPromise = MongoClient.connect(this._databaseURI, this._mongoOptions).then(
client => {
this._client = client;
return client.db(client.s.options.dbName);
}
);
}
return this._connectionPromise;
}
// For a given config object, filename, and data, store a file
// Returns a promise
createFile(filename: string, data) {
return this._connect()
.then(database => {
const gridStore = new GridStore(database, filename, 'w');
return gridStore.open();
})
.then(gridStore => {
return gridStore.write(data);
})
.then(gridStore => {
return gridStore.close();
});
}
deleteFile(filename: string) {
return this._connect()
.then(database => {
const gridStore = new GridStore(database, filename, 'r');
return gridStore.open();
})
.then(gridStore => {
return gridStore.unlink();
})
.then(gridStore => {
return gridStore.close();
});
}
getFileData(filename: string) {
return this._connect()
.then(database => {
return GridStore.exist(database, filename).then(() => {
const gridStore = new GridStore(database, filename, 'r');
return gridStore.open();
});
})
.then(gridStore => {
return gridStore.read();
});
}
getFileLocation(config, filename) {
return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
}
async handleFileStream(filename: string, req, res, contentType) {
const stream = await this._connect().then(database => {
return GridStore.exist(database, filename).then(() => {
const gridStore = new GridStore(database, filename, 'r');
return gridStore.open();
});
});
handleRangeRequest(stream, req, res, contentType);
}
handleShutdown() {
if (!this._client) {
return Promise.resolve();
}
return this._client.close(false);
}
validateFilename(filename) {
return validateFilename(filename);
}
}
// handleRangeRequest is licensed under Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0/).
// Author: LEROIB at weightingformypizza (https://weightingformypizza.wordpress.com/2015/06/24/stream-html5-media-content-like-video-audio-from-mongodb-using-express-and-gridstore/).
function handleRangeRequest(stream, req, res, contentType) {
const buffer_size = 1024 * 1024; //1024Kb
// Range request, partial stream the file
const parts = req
.get('Range')
.replace(/bytes=/, '')
.split('-');
let [start, end] = parts;
const notEnded = !end && end !== 0;
const notStarted = !start && start !== 0;
// No end provided, we want all bytes
if (notEnded) {
end = stream.length - 1;
}
// No start provided, we're reading backwards
if (notStarted) {
start = stream.length - end;
end = start + end - 1;
}
// Data exceeds the buffer_size, cap
if (end - start >= buffer_size) {
end = start + buffer_size - 1;
}
const contentLength = end - start + 1;
res.writeHead(206, {
'Content-Range': 'bytes ' + start + '-' + end + '/' + stream.length,
'Accept-Ranges': 'bytes',
'Content-Length': contentLength,
'Content-Type': contentType,
});
stream.seek(start, function () {
// Get gridFile stream
const gridFileStream = stream.stream(true);
let bufferAvail = 0;
let remainingBytesToWrite = contentLength;
let totalBytesWritten = 0;
// Write to response
gridFileStream.on('data', function (data) {
bufferAvail += data.length;
if (bufferAvail > 0) {
// slice returns the same buffer if overflowing
// safe to call in any case
const buffer = data.slice(0, remainingBytesToWrite);
// Write the buffer
res.write(buffer);
// Increment total
totalBytesWritten += buffer.length;
// Decrement remaining
remainingBytesToWrite -= data.length;
// Decrement the available buffer
bufferAvail -= buffer.length;
}
// In case of small slices, all values will be good at that point
// we've written enough, end...
if (totalBytesWritten >= contentLength) {
stream.close();
res.end();
this.destroy();
}
});
});
}
export default GridStoreAdapter;
// Note: GridStore was replaced by GridFSBucketAdapter by default in 2018 by @flovilmart
throw new Error(
'GridStoreAdapter: GridStore is no longer supported by parse server and mongodb, use GridFSBucketAdapter instead.'
);
@@ -177,7 +177,7 @@ class MongoSchemaCollection {
insertSchema(schema: any) {
return this._collection
.insertOne(schema)
.then(result => mongoSchemaToParseSchema(result.ops[0]))
.then(() => mongoSchemaToParseSchema(schema))
.catch(error => {
if (error.code === 11000) {
//Mongo's duplicate key error
@@ -479,6 +479,7 @@ export class MongoStorageAdapter implements StorageAdapter {
const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema);
return this._adaptiveCollection(className)
.then(collection => collection.insertOne(mongoObject, transactionalSession))
.then(() => ({ ops: [mongoObject] }))
.catch(error => {
if (error.code === 11000) {
// Duplicate value
@@ -517,8 +518,8 @@ export class MongoStorageAdapter implements StorageAdapter {
})
.catch(err => this.handleError(err))
.then(
({ result }) => {
if (result.n === 0) {
({ deletedCount }) => {
if (deletedCount === 0) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
}
return Promise.resolve();
@@ -2440,55 +2440,48 @@ export class PostgresStorageAdapter implements StorageAdapter {
? fieldNames.map((fieldName, index) => `lower($${index + 3}:name) varchar_pattern_ops`)
: fieldNames.map((fieldName, index) => `$${index + 3}:name`);
const qs = `CREATE INDEX IF NOT EXISTS $1:name ON $2:name (${constraintPatterns.join()})`;
const setIdempotencyFunction = options.setIdempotencyFunction !== undefined ? options.setIdempotencyFunction : false;
const setIdempotencyFunction =
options.setIdempotencyFunction !== undefined ? options.setIdempotencyFunction : false;
if (setIdempotencyFunction) {
await this.ensureIdempotencyFunctionExists(options);
}
await conn.none(qs, [indexNameOptions.name, className, ...fieldNames])
.catch(error => {
if (
error.code === PostgresDuplicateRelationError &&
error.message.includes(indexNameOptions.name)
) {
// Index already exists. Ignore error.
} else if (
error.code === PostgresUniqueIndexViolationError &&
error.message.includes(indexNameOptions.name)
) {
// Cast the error into the proper parse error
throw new Parse.Error(
Parse.Error.DUPLICATE_VALUE,
'A duplicate value for a field with unique values was provided'
);
} else {
throw error;
}
});
await conn.none(qs, [indexNameOptions.name, className, ...fieldNames]).catch(error => {
if (
error.code === PostgresDuplicateRelationError &&
error.message.includes(indexNameOptions.name)
) {
// Index already exists. Ignore error.
} else if (
error.code === PostgresUniqueIndexViolationError &&
error.message.includes(indexNameOptions.name)
) {
// Cast the error into the proper parse error
throw new Parse.Error(
Parse.Error.DUPLICATE_VALUE,
'A duplicate value for a field with unique values was provided'
);
} else {
throw error;
}
});
}
async deleteIdempotencyFunction(
options?: Object = {}
): Promise<any> {
async deleteIdempotencyFunction(options?: Object = {}): Promise<any> {
const conn = options.conn !== undefined ? options.conn : this._client;
const qs = 'DROP FUNCTION IF EXISTS idempotency_delete_expired_records()';
return conn
.none(qs)
.catch(error => {
throw error;
});
return conn.none(qs).catch(error => {
throw error;
});
}
async ensureIdempotencyFunctionExists(
options?: Object = {}
): Promise<any> {
async ensureIdempotencyFunctionExists(options?: Object = {}): Promise<any> {
const conn = options.conn !== undefined ? options.conn : this._client;
const ttlOptions = options.ttl !== undefined ? `${options.ttl} seconds` : '60 seconds';
const qs = 'CREATE OR REPLACE FUNCTION idempotency_delete_expired_records() RETURNS void LANGUAGE plpgsql AS $$ BEGIN DELETE FROM "_Idempotency" WHERE expire < NOW() - INTERVAL $1; END; $$;';
return conn
.none(qs, [ttlOptions])
.catch(error => {
throw error;
});
const qs =
'CREATE OR REPLACE FUNCTION idempotency_delete_expired_records() RETURNS void LANGUAGE plpgsql AS $$ BEGIN DELETE FROM "_Idempotency" WHERE expire < NOW() - INTERVAL $1; END; $$;';
return conn.none(qs, [ttlOptions]).catch(error => {
throw error;
});
}
}
+11 -1
View File
@@ -35,7 +35,7 @@ export class Config {
config.applicationId = applicationId;
Object.keys(cacheInfo).forEach(key => {
if (key == 'databaseController') {
config.database = new DatabaseController(cacheInfo.databaseController.adapter);
config.database = new DatabaseController(cacheInfo.databaseController.adapter, config);
} else {
config[key] = cacheInfo[key];
}
@@ -78,6 +78,7 @@ export class Config {
security,
enforcePrivateUsers,
schema,
requestKeywordDenylist,
}) {
if (masterKey === readOnlyMasterKey) {
throw new Error('masterKey and readOnlyMasterKey should be different');
@@ -116,6 +117,15 @@ export class Config {
this.validateSecurityOptions(security);
this.validateSchemaOptions(schema);
this.validateEnforcePrivateUsers(enforcePrivateUsers);
this.validateRequestKeywordDenylist(requestKeywordDenylist);
}
static validateRequestKeywordDenylist(requestKeywordDenylist) {
if (requestKeywordDenylist === undefined) {
requestKeywordDenylist = requestKeywordDenylist.default;
} else if (!Array.isArray(requestKeywordDenylist)) {
throw 'Parse Server option requestKeywordDenylist must be an array.';
}
}
static validateEnforcePrivateUsers(enforcePrivateUsers) {
+66 -43
View File
@@ -11,12 +11,14 @@ import intersect from 'intersect';
// @flow-disable-next
import deepcopy from 'deepcopy';
import logger from '../logger';
import Utils from '../Utils';
import * as SchemaController from './SchemaController';
import { StorageAdapter } from '../Adapters/Storage/StorageAdapter';
import MongoStorageAdapter from '../Adapters/Storage/Mongo/MongoStorageAdapter';
import PostgresStorageAdapter from '../Adapters/Storage/Postgres/PostgresStorageAdapter';
import SchemaCache from '../Adapters/Cache/SchemaCache';
import type { LoadSchemaOptions } from './types';
import type { ParseServerOptions } from '../Options';
import type { QueryOptions, FullQueryOptions } from '../Adapters/Storage/StorageAdapter';
function addWriteACL(query, acl) {
@@ -258,41 +260,6 @@ const isSpecialUpdateKey = key => {
return specialKeysForUpdate.indexOf(key) >= 0;
};
function expandResultOnKeyPath(object, key, value) {
if (key.indexOf('.') < 0) {
object[key] = value[key];
return object;
}
const path = key.split('.');
const firstKey = path[0];
const nextPath = path.slice(1).join('.');
object[firstKey] = expandResultOnKeyPath(object[firstKey] || {}, nextPath, value[firstKey]);
delete object[key];
return object;
}
function sanitizeDatabaseResult(originalObject, result): Promise<any> {
const response = {};
if (!result) {
return Promise.resolve(response);
}
Object.keys(originalObject).forEach(key => {
const keyUpdate = originalObject[key];
// determine if that was an op
if (
keyUpdate &&
typeof keyUpdate === 'object' &&
keyUpdate.__op &&
['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1
) {
// only valid ops that produce an actionable result
// the op may have happend on a keypath
expandResultOnKeyPath(response, key, result);
}
});
return Promise.resolve(response);
}
function joinTableName(className, key) {
return `_Join:${key}:${className}`;
}
@@ -395,19 +362,21 @@ const relationSchema = {
class DatabaseController {
adapter: StorageAdapter;
idempotencyOptions: any;
schemaCache: any;
schemaPromise: ?Promise<SchemaController.SchemaController>;
_transactionalSession: ?any;
options: ParseServerOptions;
idempotencyOptions: any;
constructor(adapter: StorageAdapter, idempotencyOptions?: Object = {}) {
constructor(adapter: StorageAdapter, options: ParseServerOptions) {
this.adapter = adapter;
this.idempotencyOptions = idempotencyOptions;
// We don't want a mutable this.schema, because then you could have
// one request that uses different schemas for different parts of
// it. Instead, use loadSchema to get a schema.
this.options = options || {};
this.idempotencyOptions = this.options.idempotencyOptions || {};
// Prevent mutable this.schema, otherwise one request could use
// multiple schemas, so instead use loadSchema to get a schema.
this.schemaPromise = null;
this._transactionalSession = null;
this.options = options;
}
collectionExists(className: string): Promise<boolean> {
@@ -646,7 +615,7 @@ class DatabaseController {
if (skipSanitization) {
return Promise.resolve(result);
}
return sanitizeDatabaseResult(originalUpdate, result);
return this._sanitizeDatabaseResult(originalUpdate, result);
});
});
}
@@ -873,7 +842,7 @@ class DatabaseController {
object,
relationUpdates
).then(() => {
return sanitizeDatabaseResult(originalObject, result.ops[0]);
return this._sanitizeDatabaseResult(originalObject, result.ops[0]);
});
});
});
@@ -1782,6 +1751,60 @@ class DatabaseController {
await this.adapter.updateSchemaWithIndexes();
}
_expandResultOnKeyPath(object: any, key: string, value: any): any {
if (key.indexOf('.') < 0) {
object[key] = value[key];
return object;
}
const path = key.split('.');
const firstKey = path[0];
const nextPath = path.slice(1).join('.');
// Scan request data for denied keywords
if (this.options && this.options.requestKeywordDenylist) {
// Scan request data for denied keywords
for (const keyword of this.options.requestKeywordDenylist) {
const match = Utils.objectContainsKeyValue({ firstKey: undefined }, keyword.key, undefined);
if (match) {
throw new Parse.Error(
Parse.Error.INVALID_KEY_NAME,
`Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
);
}
}
}
object[firstKey] = this._expandResultOnKeyPath(
object[firstKey] || {},
nextPath,
value[firstKey]
);
delete object[key];
return object;
}
_sanitizeDatabaseResult(originalObject: any, result: any): Promise<any> {
const response = {};
if (!result) {
return Promise.resolve(response);
}
Object.keys(originalObject).forEach(key => {
const keyUpdate = originalObject[key];
// determine if that was an op
if (
keyUpdate &&
typeof keyUpdate === 'object' &&
keyUpdate.__op &&
['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1
) {
// only valid ops that produce an actionable result
// the op may have happened on a keypath
this._expandResultOnKeyPath(response, key, result);
}
});
return Promise.resolve(response);
}
static _validateQuery: any => void;
}
+2 -2
View File
@@ -142,7 +142,7 @@ export function getLiveQueryController(options: ParseServerOptions): LiveQueryCo
}
export function getDatabaseController(options: ParseServerOptions): DatabaseController {
const { databaseURI, collectionPrefix, databaseOptions, idempotencyOptions } = options;
const { databaseURI, collectionPrefix, databaseOptions } = options;
let { databaseAdapter } = options;
if (
(databaseOptions ||
@@ -156,7 +156,7 @@ export function getDatabaseController(options: ParseServerOptions): DatabaseCont
} else {
databaseAdapter = loadAdapter(databaseAdapter);
}
return new DatabaseController(databaseAdapter, idempotencyOptions);
return new DatabaseController(databaseAdapter, options);
}
export function getHooksController(
+13 -32
View File
@@ -10,7 +10,7 @@ import { ParsePubSub } from './ParsePubSub';
import SchemaController from '../Controllers/SchemaController';
import _ from 'lodash';
import { v4 as uuidv4 } from 'uuid';
import { runLiveQueryEventHandlers, getTrigger, runTrigger, toJSONwithObjects } from '../triggers';
import { runLiveQueryEventHandlers, getTrigger, runTrigger, resolveError, toJSONwithObjects } from '../triggers';
import { getAuthForSessionToken, Auth } from '../Auth';
import { getCacheController } from '../Controllers';
import LRU from 'lru-cache';
@@ -194,14 +194,9 @@ class ParseLiveQueryServer {
delete deletedParseObject.authData;
}
client.pushDelete(requestId, deletedParseObject);
} catch (error) {
Client.pushError(
client.parseWebSocket,
error.code || Parse.Error.SCRIPT_FAILED,
error.message || error,
false,
requestId
);
} catch (e) {
const error = resolveError(e);
Client.pushError(client.parseWebSocket, error.code, error.message, false, requestId);
logger.error(
`Failed running afterLiveQueryEvent on class ${className} for event ${res.event} with session ${res.sessionToken} with:\n Error: ` +
JSON.stringify(error)
@@ -358,14 +353,9 @@ class ParseLiveQueryServer {
if (client[functionName]) {
client[functionName](requestId, currentParseObject, originalParseObject);
}
} catch (error) {
Client.pushError(
client.parseWebSocket,
error.code || Parse.Error.SCRIPT_FAILED,
error.message || error,
false,
requestId
);
} catch (e) {
const error = resolveError(e);
Client.pushError(client.parseWebSocket, error.code, error.message, false, requestId);
logger.error(
`Failed running afterLiveQueryEvent on class ${className} for event ${res.event} with session ${res.sessionToken} with:\n Error: ` +
JSON.stringify(error)
@@ -681,13 +671,9 @@ class ParseLiveQueryServer {
logger.info(`Create new client: ${parseWebsocket.clientId}`);
client.pushConnect();
runLiveQueryEventHandlers(req);
} catch (error) {
Client.pushError(
parseWebsocket,
error.code || Parse.Error.SCRIPT_FAILED,
error.message || error,
false
);
} catch (e) {
const error = resolveError(e);
Client.pushError(parseWebsocket, error.code, error.message, false);
logger.error(
`Failed running beforeConnect for session ${request.sessionToken} with:\n Error: ` +
JSON.stringify(error)
@@ -827,16 +813,11 @@ class ParseLiveQueryServer {
installationId: client.installationId,
});
} catch (e) {
Client.pushError(
parseWebsocket,
e.code || Parse.Error.SCRIPT_FAILED,
e.message || e,
false,
request.requestId
);
const error = resolveError(e);
Client.pushError(parseWebsocket, error.code, error.message, false, request.requestId);
logger.error(
`Failed running beforeSubscribe on ${className} for session ${request.sessionToken} with:\n Error: ` +
JSON.stringify(e)
JSON.stringify(error)
);
}
}
+18
View File
@@ -350,6 +350,24 @@ module.exports.ParseServerOptions = {
env: 'PARSE_SERVER_READ_ONLY_MASTER_KEY',
help: 'Read-only key, which has the same capabilities as MasterKey without writes',
},
requestKeywordDenylist: {
env: 'PARSE_SERVER_REQUEST_KEYWORD_DENYLIST',
help:
'An array of keys and values that are prohibited in database read and write requests to prevent potential security vulnerabilities. It is possible to specify only a key (`{"key":"..."}`), only a value (`{"value":"..."}`) or a key-value pair (`{"key":"...","value":"..."}`). The specification can use the following types: `boolean`, `numeric` or `string`, where `string` will be interpreted as a regex notation. Request data is deep-scanned for matching definitions to detect also any nested occurrences. Defaults are patterns that are likely to be used in malicious requests. Setting this option will override the default patterns.',
action: parsers.arrayParser,
default: [
{
key: '_bsontype',
value: 'Code',
},
{
key: 'constructor',
},
{
key: '__proto__',
},
],
},
restAPIKey: {
env: 'PARSE_SERVER_REST_API_KEY',
help: 'Key for REST calls',
+1
View File
@@ -64,6 +64,7 @@
* @property {String} publicServerURL Public URL to your parse server with http:// or https://.
* @property {Any} push Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications
* @property {String} readOnlyMasterKey Read-only key, which has the same capabilities as MasterKey without writes
* @property {RequestKeywordDenylist[]} requestKeywordDenylist An array of keys and values that are prohibited in database read and write requests to prevent potential security vulnerabilities. It is possible to specify only a key (`{"key":"..."}`), only a value (`{"value":"..."}`) or a key-value pair (`{"key":"...","value":"..."}`). The specification can use the following types: `boolean`, `numeric` or `string`, where `string` will be interpreted as a regex notation. Request data is deep-scanned for matching definitions to detect also any nested occurrences. Defaults are patterns that are likely to be used in malicious requests. Setting this option will override the default patterns.
* @property {String} restAPIKey Key for REST calls
* @property {Boolean} revokeSessionOnPasswordReset When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.
* @property {Boolean} scheduledPush Configuration for push scheduling, defaults to false.
+7
View File
@@ -14,6 +14,10 @@ type Adapter<T> = string | any | T;
type NumberOrBoolean = number | boolean;
type NumberOrString = number | string;
type ProtectedFields = any;
type RequestKeywordDenylist = {
key: string | any,
value: any,
};
export interface ParseServerOptions {
/* Your Parse Application ID
@@ -252,6 +256,9 @@ export interface ParseServerOptions {
/* Set to true if new users should be created without public read and write access.
:DEFAULT: false */
enforcePrivateUsers: ?boolean;
/* An array of keys and values that are prohibited in database read and write requests to prevent potential security vulnerabilities. It is possible to specify only a key (`{"key":"..."}`), only a value (`{"value":"..."}`) or a key-value pair (`{"key":"...","value":"..."}`). The specification can use the following types: `boolean`, `numeric` or `string`, where `string` will be interpreted as a regex notation. Request data is deep-scanned for matching definitions to detect also any nested occurrences. Defaults are patterns that are likely to be used in malicious requests. Setting this option will override the default patterns.
:DEFAULT: [{"key":"_bsontype","value":"Code"},{"key":"constructor"},{"key":"__proto__"}] */
requestKeywordDenylist: ?(RequestKeywordDenylist[]);
}
export interface SecurityOptions {
+14
View File
@@ -6,6 +6,7 @@ var SchemaController = require('./Controllers/SchemaController');
var deepcopy = require('deepcopy');
const Auth = require('./Auth');
const Utils = require('./Utils');
var cryptoUtils = require('./cryptoUtils');
var passwordCrypto = require('./password');
var Parse = require('parse/node');
@@ -61,6 +62,19 @@ function RestWrite(config, auth, className, query, data, originalData, clientSDK
}
}
if (this.config.requestKeywordDenylist) {
// Scan request data for denied keywords
for (const keyword of this.config.requestKeywordDenylist) {
const match = Utils.objectContainsKeyValue(data, keyword.key, keyword.value);
if (match) {
throw new Parse.Error(
Parse.Error.INVALID_KEY_NAME,
`Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
);
}
}
}
// When the operation is complete, this.response may have several
// fields.
// response: the actual data to be returned
+2 -1
View File
@@ -83,7 +83,8 @@ export class ClassesRouter extends PromiseRouter {
this.className(req),
req.params.objectId,
options,
req.info.clientSDK
req.info.clientSDK,
req.info.context
)
.then(response => {
if (!response.results || response.results.length == 0) {
+26
View File
@@ -332,6 +332,32 @@ class Utils {
};
}
}
/**
* Deep-scans an object for a matching key/value definition.
* @param {Object} obj The object to scan.
* @param {String | undefined} key The key to match, or undefined if only the value should be matched.
* @param {any | undefined} value The value to match, or undefined if only the key should be matched.
* @returns {Boolean} True if a match was found, false otherwise.
*/
static objectContainsKeyValue(obj, key, value) {
const isMatch = (a, b) => (typeof a === 'string' && new RegExp(b).test(a)) || a === b;
const isKeyMatch = k => isMatch(k, key);
const isValueMatch = v => isMatch(v, value);
for (const [k, v] of Object.entries(obj)) {
if (key !== undefined && value === undefined && isKeyMatch(k)) {
return true;
} else if (key === undefined && value !== undefined && isValueMatch(v)) {
return true;
} else if (key !== undefined && value !== undefined && isKeyMatch(k) && isValueMatch(v)) {
return true;
}
if (['[object Object]', '[object Array]'].includes(Object.prototype.toString.call(v))) {
return Utils.objectContainsKeyValue(v, key, value);
}
}
return false;
}
}
module.exports = Utils;
+1 -1
View File
@@ -13,7 +13,7 @@ function mountOnto(router) {
function parseURL(urlString) {
try {
return new URL(urlString);
} catch(error) {
} catch (error) {
return undefined;
}
}
+6 -1
View File
@@ -432,7 +432,12 @@ export function promiseEnforceMasterKeyAccess(request) {
*/
export function promiseEnsureIdempotency(req) {
// Enable feature only for MongoDB
if (!((req.config.database.adapter instanceof MongoStorageAdapter) || (req.config.database.adapter instanceof PostgresStorageAdapter))) {
if (
!(
req.config.database.adapter instanceof MongoStorageAdapter ||
req.config.database.adapter instanceof PostgresStorageAdapter
)
) {
return Promise.resolve();
}
// Get parameters