mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
128023bf88 | ||
|
|
85b63eef17 | ||
|
|
59d86c8a77 | ||
|
|
4d37dee9cc | ||
|
|
388cd3e1d3 | ||
|
|
7298641f14 | ||
|
|
f66ece53b1 | ||
|
|
13ee52f0d1 | ||
|
|
b86906f303 | ||
|
|
6613f8f403 | ||
|
|
8b7fe699c1 | ||
|
|
435f0d1a42 | ||
|
|
3bb3a8090d | ||
|
|
e944616ff7 | ||
|
|
1a2da4055a | ||
|
|
2a63129ff6 | ||
|
|
9552a4cbee | ||
|
|
9fd707092e | ||
|
|
453a9879ef | ||
|
|
e08af3bbf7 | ||
|
|
d0cb857e37 | ||
|
|
388ecde6a6 | ||
|
|
cb26e20463 | ||
|
|
1e7d7447dc | ||
|
|
5d09a1fd81 | ||
|
|
71a34eccc2 | ||
|
|
90a1e4a200 | ||
|
|
4d86ace2cc | ||
|
|
c4db9e6ae4 | ||
|
|
edad6329cc | ||
|
|
b5e550b593 | ||
|
|
f4c620b24b | ||
|
|
ef63c63097 | ||
|
|
a9132a6bcc | ||
|
|
b0b3051e64 | ||
|
|
0319cee2db | ||
|
|
901cff5edd | ||
|
|
4b04c2222c | ||
|
|
76dbcc5f7e | ||
|
|
49006e87e9 | ||
|
|
50d9a8fad1 | ||
|
|
35cadf9b83 | ||
|
|
69aba3bd3b | ||
|
|
16f31504b3 | ||
|
|
0fa82a54fe | ||
|
|
5fa58fe774 |
@@ -175,7 +175,7 @@ jobs:
|
||||
NODE_VERSION: 18.20.4
|
||||
fail-fast: false
|
||||
name: ${{ matrix.name }}
|
||||
timeout-minutes: 15
|
||||
timeout-minutes: 20
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
redis:
|
||||
@@ -238,11 +238,11 @@ jobs:
|
||||
POSTGRES_IMAGE: postgis/postgis:15-3.4
|
||||
NODE_VERSION: 22.4.1
|
||||
- name: PostgreSQL 16, PostGIS 3.4
|
||||
POSTGRES_IMAGE: postgis/postgis:15-3.4
|
||||
POSTGRES_IMAGE: postgis/postgis:16-3.4
|
||||
NODE_VERSION: 22.4.1
|
||||
fail-fast: false
|
||||
name: ${{ matrix.name }}
|
||||
timeout-minutes: 15
|
||||
timeout-minutes: 20
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
redis:
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
---
|
||||
|
||||
[](https://github.com/parse-community/parse-server/actions?query=workflow%3Aci+branch%3Aalpha)
|
||||
[](https://github.com/parse-community/parse-server/actions?query=workflow%3Aci+branch%3Abeta)
|
||||
[](https://github.com/parse-community/parse-server/actions?query=workflow%3Aci+branch%3Arelease)
|
||||
[](https://github.com/parse-community/parse-server/actions/workflows/ci.yml?query=workflow%3Aci+branch%3Aalpha)
|
||||
[](https://github.com/parse-community/parse-server/actions/workflows/ci.yml?query=workflow%3Aci+branch%3Abeta)
|
||||
[](https://github.com/parse-community/parse-server/actions/workflows/ci.yml?query=workflow%3Aci+branch%3Arelease)
|
||||
[](https://snyk.io/test/github/parse-community/parse-server)
|
||||
[](https://app.codecov.io/github/parse-community/parse-server/tree/alpha)
|
||||
[](https://github.com/parse-community/parse-dashboard/releases)
|
||||
@@ -276,6 +276,7 @@ We have provided a basic [Node.js application](https://github.com/parse-communit
|
||||
* [Back4app](https://www.back4app.com/docs/get-started/welcome)
|
||||
* [Glitch](https://glitch.com/edit/#!/parse-server)
|
||||
* [Flynn](https://flynn.io/blog/parse-apps-on-flynn)
|
||||
* [Elestio](https://elest.io/open-source/parse)
|
||||
|
||||
### Parse Server + Express
|
||||
|
||||
|
||||
@@ -1,3 +1,52 @@
|
||||
# [7.3.0-alpha.9](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.8...7.3.0-alpha.9) (2024-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9317](https://github.com/parse-community/parse-server/issues/9317)) ([13ee52f](https://github.com/parse-community/parse-server/commit/13ee52f0d19ef3a3524b3d79aea100e587eb3cfc))
|
||||
|
||||
# [7.3.0-alpha.8](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.7...7.3.0-alpha.8) (2024-09-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Security upgrade path-to-regexp from 6.2.1 to 6.3.0 ([#9314](https://github.com/parse-community/parse-server/issues/9314)) ([8b7fe69](https://github.com/parse-community/parse-server/commit/8b7fe699c1c376ecd8cc1c97cce8e704ee41f28a))
|
||||
|
||||
# [7.3.0-alpha.7](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.6...7.3.0-alpha.7) (2024-08-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add support for asynchronous invocation of `FilesAdapter.getFileLocation` ([#9271](https://github.com/parse-community/parse-server/issues/9271)) ([1a2da40](https://github.com/parse-community/parse-server/commit/1a2da4055abe831b3017172fb75e16d7a8093873))
|
||||
|
||||
# [7.3.0-alpha.6](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.5...7.3.0-alpha.6) (2024-07-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add Cloud Code triggers `Parse.Cloud.beforeSave` and `Parse.Cloud.afterSave` for Parse Config ([#9232](https://github.com/parse-community/parse-server/issues/9232)) ([90a1e4a](https://github.com/parse-community/parse-server/commit/90a1e4a200423d644efb3f0ba2fba4b99f5cf954))
|
||||
|
||||
# [7.3.0-alpha.5](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.4...7.3.0-alpha.5) (2024-07-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Parse Server option `maxLogFiles` doesn't recognize day duration literals such as `1d` to mean 1 day ([#9215](https://github.com/parse-community/parse-server/issues/9215)) ([0319cee](https://github.com/parse-community/parse-server/commit/0319cee2dbf65e90bad377af1ed14ea25c595bf5))
|
||||
|
||||
# [7.3.0-alpha.4](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.3...7.3.0-alpha.4) (2024-07-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add atomic operations for Cloud Config parameters ([#9219](https://github.com/parse-community/parse-server/issues/9219)) ([35cadf9](https://github.com/parse-community/parse-server/commit/35cadf9b8324879fb7309ba5d7ea46f2c722d614))
|
||||
|
||||
# [7.3.0-alpha.3](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.2...7.3.0-alpha.3) (2024-07-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Parse Server installation fails due to post install script incorrectly parsing required min. Node version ([#9216](https://github.com/parse-community/parse-server/issues/9216)) ([0fa82a5](https://github.com/parse-community/parse-server/commit/0fa82a54fe38ec14e8054339285d3db71a8624c8))
|
||||
|
||||
# [7.3.0-alpha.2](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.1...7.3.0-alpha.2) (2024-07-17)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
# [7.3.0-beta.1](https://github.com/parse-community/parse-server/compare/7.2.0...7.3.0-beta.1) (2024-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9317](https://github.com/parse-community/parse-server/issues/9317)) ([13ee52f](https://github.com/parse-community/parse-server/commit/13ee52f0d19ef3a3524b3d79aea100e587eb3cfc))
|
||||
* Parse Server `databaseOptions` nested keys incorrectly identified as invalid ([#9213](https://github.com/parse-community/parse-server/issues/9213)) ([77206d8](https://github.com/parse-community/parse-server/commit/77206d804443cfc1618c24f8961bd677de9920c0))
|
||||
* Parse Server installation fails due to post install script incorrectly parsing required min. Node version ([#9216](https://github.com/parse-community/parse-server/issues/9216)) ([0fa82a5](https://github.com/parse-community/parse-server/commit/0fa82a54fe38ec14e8054339285d3db71a8624c8))
|
||||
* Parse Server option `maxLogFiles` doesn't recognize day duration literals such as `1d` to mean 1 day ([#9215](https://github.com/parse-community/parse-server/issues/9215)) ([0319cee](https://github.com/parse-community/parse-server/commit/0319cee2dbf65e90bad377af1ed14ea25c595bf5))
|
||||
* Security upgrade path-to-regexp from 6.2.1 to 6.3.0 ([#9314](https://github.com/parse-community/parse-server/issues/9314)) ([8b7fe69](https://github.com/parse-community/parse-server/commit/8b7fe699c1c376ecd8cc1c97cce8e704ee41f28a))
|
||||
|
||||
### Features
|
||||
|
||||
* Add atomic operations for Cloud Config parameters ([#9219](https://github.com/parse-community/parse-server/issues/9219)) ([35cadf9](https://github.com/parse-community/parse-server/commit/35cadf9b8324879fb7309ba5d7ea46f2c722d614))
|
||||
* Add Cloud Code triggers `Parse.Cloud.beforeSave` and `Parse.Cloud.afterSave` for Parse Config ([#9232](https://github.com/parse-community/parse-server/issues/9232)) ([90a1e4a](https://github.com/parse-community/parse-server/commit/90a1e4a200423d644efb3f0ba2fba4b99f5cf954))
|
||||
* Add Node 22 support ([#9187](https://github.com/parse-community/parse-server/issues/9187)) ([7778471](https://github.com/parse-community/parse-server/commit/7778471999c7e42236ce404229660d80ecc2acd6))
|
||||
* Add support for asynchronous invocation of `FilesAdapter.getFileLocation` ([#9271](https://github.com/parse-community/parse-server/issues/9271)) ([1a2da40](https://github.com/parse-community/parse-server/commit/1a2da4055abe831b3017172fb75e16d7a8093873))
|
||||
|
||||
# [7.2.0-beta.1](https://github.com/parse-community/parse-server/compare/7.1.0...7.2.0-beta.1) (2024-07-09)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
# [7.3.0](https://github.com/parse-community/parse-server/compare/7.2.0...7.3.0) (2024-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9317](https://github.com/parse-community/parse-server/issues/9317)) ([13ee52f](https://github.com/parse-community/parse-server/commit/13ee52f0d19ef3a3524b3d79aea100e587eb3cfc))
|
||||
* Parse Server `databaseOptions` nested keys incorrectly identified as invalid ([#9213](https://github.com/parse-community/parse-server/issues/9213)) ([77206d8](https://github.com/parse-community/parse-server/commit/77206d804443cfc1618c24f8961bd677de9920c0))
|
||||
* Parse Server installation fails due to post install script incorrectly parsing required min. Node version ([#9216](https://github.com/parse-community/parse-server/issues/9216)) ([0fa82a5](https://github.com/parse-community/parse-server/commit/0fa82a54fe38ec14e8054339285d3db71a8624c8))
|
||||
* Parse Server option `maxLogFiles` doesn't recognize day duration literals such as `1d` to mean 1 day ([#9215](https://github.com/parse-community/parse-server/issues/9215)) ([0319cee](https://github.com/parse-community/parse-server/commit/0319cee2dbf65e90bad377af1ed14ea25c595bf5))
|
||||
* Security upgrade path-to-regexp from 6.2.1 to 6.3.0 ([#9314](https://github.com/parse-community/parse-server/issues/9314)) ([8b7fe69](https://github.com/parse-community/parse-server/commit/8b7fe699c1c376ecd8cc1c97cce8e704ee41f28a))
|
||||
|
||||
### Features
|
||||
|
||||
* Add atomic operations for Cloud Config parameters ([#9219](https://github.com/parse-community/parse-server/issues/9219)) ([35cadf9](https://github.com/parse-community/parse-server/commit/35cadf9b8324879fb7309ba5d7ea46f2c722d614))
|
||||
* Add Cloud Code triggers `Parse.Cloud.beforeSave` and `Parse.Cloud.afterSave` for Parse Config ([#9232](https://github.com/parse-community/parse-server/issues/9232)) ([90a1e4a](https://github.com/parse-community/parse-server/commit/90a1e4a200423d644efb3f0ba2fba4b99f5cf954))
|
||||
* Add Node 22 support ([#9187](https://github.com/parse-community/parse-server/issues/9187)) ([7778471](https://github.com/parse-community/parse-server/commit/7778471999c7e42236ce404229660d80ecc2acd6))
|
||||
* Add support for asynchronous invocation of `FilesAdapter.getFileLocation` ([#9271](https://github.com/parse-community/parse-server/issues/9271)) ([1a2da40](https://github.com/parse-community/parse-server/commit/1a2da4055abe831b3017172fb75e16d7a8093873))
|
||||
|
||||
# [7.2.0](https://github.com/parse-community/parse-server/compare/7.1.0...7.2.0) (2024-07-09)
|
||||
|
||||
|
||||
|
||||
Generated
+1868
-1665
File diff suppressed because it is too large
Load Diff
+19
-19
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "parse-server",
|
||||
"version": "7.3.0-alpha.2",
|
||||
"version": "7.3.0",
|
||||
"description": "An express module providing a Parse-compatible API server",
|
||||
"main": "lib/index.js",
|
||||
"repository": {
|
||||
@@ -19,24 +19,24 @@
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@apollo/server": "4.10.1",
|
||||
"@apollo/server": "4.10.5",
|
||||
"@babel/eslint-parser": "7.24.7",
|
||||
"@graphql-tools/merge": "9.0.3",
|
||||
"@graphql-tools/schema": "10.0.3",
|
||||
"@graphql-tools/merge": "9.0.4",
|
||||
"@graphql-tools/schema": "10.0.4",
|
||||
"@graphql-tools/utils": "8.12.0",
|
||||
"@parse/fs-files-adapter": "3.0.0",
|
||||
"@parse/push-adapter": "6.4.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.20.2",
|
||||
"commander": "12.0.0",
|
||||
"body-parser": "1.20.3",
|
||||
"commander": "12.1.0",
|
||||
"cors": "2.8.5",
|
||||
"deepcopy": "2.1.0",
|
||||
"express": "4.19.2",
|
||||
"express": "4.21.0",
|
||||
"express-rate-limit": "7.3.1",
|
||||
"follow-redirects": "1.15.6",
|
||||
"graphql": "16.8.1",
|
||||
"graphql-list-fields": "2.0.4",
|
||||
"graphql-relay": "0.10.1",
|
||||
"graphql-relay": "0.10.2",
|
||||
"graphql-tag": "2.12.6",
|
||||
"graphql-upload": "15.0.2",
|
||||
"intersect": "1.0.1",
|
||||
@@ -48,16 +48,16 @@
|
||||
"mime": "3.0.0",
|
||||
"mongodb": "5.9.0",
|
||||
"mustache": "4.2.0",
|
||||
"otpauth": "9.2.2",
|
||||
"otpauth": "9.3.1",
|
||||
"parse": "5.3.0",
|
||||
"path-to-regexp": "6.2.1",
|
||||
"pg-monitor": "2.0.0",
|
||||
"pg-promise": "11.7.8",
|
||||
"path-to-regexp": "6.3.0",
|
||||
"pg-monitor": "2.1.0",
|
||||
"pg-promise": "11.9.1",
|
||||
"pluralize": "8.0.0",
|
||||
"punycode": "2.3.1",
|
||||
"rate-limit-redis": "4.2.0",
|
||||
"redis": "4.6.13",
|
||||
"semver": "7.6.0",
|
||||
"semver": "7.6.3",
|
||||
"subscriptions-transport-ws": "0.11.0",
|
||||
"tv4": "1.3.0",
|
||||
"uuid": "10.0.0",
|
||||
@@ -67,12 +67,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "1.10.1",
|
||||
"@apollo/client": "3.9.11",
|
||||
"@apollo/client": "3.11.1",
|
||||
"@babel/cli": "7.24.8",
|
||||
"@babel/core": "7.24.7",
|
||||
"@babel/plugin-proposal-object-rest-spread": "7.20.7",
|
||||
"@babel/plugin-transform-flow-strip-types": "7.24.7",
|
||||
"@babel/preset-env": "7.24.0",
|
||||
"@babel/preset-env": "7.24.8",
|
||||
"@saithodev/semantic-release-backmerge": "4.0.1",
|
||||
"@semantic-release/changelog": "6.0.3",
|
||||
"@semantic-release/commit-analyzer": "13.0.0",
|
||||
@@ -85,7 +85,7 @@
|
||||
"clean-jsdoc-theme": "4.3.0",
|
||||
"cross-env": "7.0.2",
|
||||
"deep-diff": "1.0.2",
|
||||
"eslint": "8.26.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-flowtype": "8.0.3",
|
||||
"flow-bin": "0.119.1",
|
||||
"form-data": "4.0.0",
|
||||
@@ -100,13 +100,13 @@
|
||||
"madge": "7.0.0",
|
||||
"mock-files-adapter": "file:spec/dependencies/mock-files-adapter",
|
||||
"mock-mail-adapter": "file:spec/dependencies/mock-mail-adapter",
|
||||
"mongodb-runner": "5.5.4",
|
||||
"mongodb-runner": "5.6.4",
|
||||
"node-abort-controller": "3.1.1",
|
||||
"node-fetch": "3.2.10",
|
||||
"nyc": "15.1.0",
|
||||
"prettier": "2.0.5",
|
||||
"semantic-release": "24.0.0",
|
||||
"yaml": "1.10.0"
|
||||
"yaml": "2.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
"ci:check": "node ./ci/ciCheck.js",
|
||||
@@ -139,7 +139,7 @@
|
||||
"madge:circular": "node_modules/.bin/madge ./src --circular"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 19 || 20 || 22"
|
||||
"node": ">=18.0.0 <19.0.0 || >=19.0.0 <20.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"parse-server": "bin/parse-server"
|
||||
|
||||
+34
-46
@@ -1,50 +1,38 @@
|
||||
const pkg = require('./package.json');
|
||||
const message = `
|
||||
1111111111
|
||||
1111111111111111
|
||||
1111111111111111111111
|
||||
11111111111111111111111111
|
||||
111111111111111 11111111
|
||||
1111111111111 111 111111
|
||||
1111111111111 111111111 111111
|
||||
111111111111 11111111111 111111
|
||||
1111111111111 11111111111 111111
|
||||
1111111111111 1111111111 111111
|
||||
1111111111111111111111111 1111111
|
||||
11111111 11111111
|
||||
111111 111 1111111111111111111
|
||||
11111 11111 111111111111111111
|
||||
11111 1 11111111111111111
|
||||
111111 111111111111111111
|
||||
11111111111111111111111111
|
||||
1111111111111111111111
|
||||
111111111111111111
|
||||
11111111111
|
||||
|
||||
Thank you for using Parse Platform!
|
||||
https://parseplatform.org
|
||||
|
||||
Please consider donating to help us maintain
|
||||
this package:
|
||||
|
||||
const version = parseFloat(process.version.substring(1));
|
||||
const minimum = parseFloat(pkg.engines.node.match(/\d+/g).join('.'));
|
||||
👉 https://opencollective.com/parse-server 👈
|
||||
|
||||
module.exports = function () {
|
||||
const openCollective = `
|
||||
1111111111
|
||||
1111111111111111
|
||||
1111111111111111111111
|
||||
11111111111111111111111111
|
||||
111111111111111 11111111
|
||||
1111111111111 111111
|
||||
1111111111111 111111111 111111
|
||||
111111111111 11111111111 111111
|
||||
1111111111111 11111111111 111111
|
||||
1111111111111 1111111111 111111
|
||||
1111111111111111111111111 1111111
|
||||
11111111 11111111
|
||||
111111 1111111111111111111
|
||||
11111 11111 111111111111111111
|
||||
11111 11111111111111111
|
||||
111111 111111111111111111
|
||||
11111111111111111111111111
|
||||
1111111111111111111111
|
||||
111111111111111111
|
||||
11111111111
|
||||
`;
|
||||
|
||||
function main() {
|
||||
process.stdout.write(message);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
Thanks for installing parse 🙏
|
||||
Please consider donating to our open collective
|
||||
to help us maintain this package.
|
||||
|
||||
👉 https://opencollective.com/parse-server
|
||||
|
||||
`;
|
||||
process.stdout.write(openCollective);
|
||||
if (version >= minimum) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const errorMessage = `
|
||||
⚠️ parse-server requires at least node@${minimum}!
|
||||
You have node@${version}
|
||||
|
||||
`;
|
||||
|
||||
process.stdout.write(errorMessage);
|
||||
process.exit(1);
|
||||
};
|
||||
module.exports = main;
|
||||
|
||||
@@ -161,6 +161,9 @@ function mapperFor(elt, t) {
|
||||
if (type == 'NumberOrBoolean') {
|
||||
return wrap(t.identifier('numberOrBooleanParser'));
|
||||
}
|
||||
if (type == 'NumberOrString') {
|
||||
return t.callExpression(wrap(t.identifier('numberOrStringParser')), [t.stringLiteral(elt.name)]);
|
||||
}
|
||||
if (type === 'StringOrStringArray') {
|
||||
return wrap(t.identifier('arrayParser'));
|
||||
}
|
||||
@@ -212,6 +215,9 @@ function parseDefaultValue(elt, value, t) {
|
||||
if (type == 'NumberOrBoolean') {
|
||||
literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
|
||||
}
|
||||
if (type == 'NumberOrString') {
|
||||
literalValue = t.numericLiteral(parsers.numberOrStringParser('')(value));
|
||||
}
|
||||
|
||||
if (nestedOptionTypes.includes(type)) {
|
||||
const object = parsers.objectParser(value);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"expectAsync": true,
|
||||
"notEqual": true,
|
||||
"it_id": true,
|
||||
"fit_id": true,
|
||||
"it_only_db": true,
|
||||
"it_only_mongodb_version": true,
|
||||
"it_only_postgres_version": true,
|
||||
@@ -27,6 +28,7 @@
|
||||
"fit_exclude_mongodb_version": true,
|
||||
"fit_exclude_node_version": true,
|
||||
"it_exclude_dbs": true,
|
||||
"fit_exclude_dbs": true,
|
||||
"describe_only_db": true,
|
||||
"fdescribe_only_db": true,
|
||||
"describe_only": true,
|
||||
|
||||
@@ -317,7 +317,7 @@ describe('AudiencesRouter', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('should support legacy parse.com audience fields', done => {
|
||||
it_id('af1111b5-3251-4b40-8f06-fb0fc624fa91')(it_exclude_dbs(['postgres']))('should support legacy parse.com audience fields', done => {
|
||||
const database = Config.get(Parse.applicationId).database.adapter.database;
|
||||
const now = new Date();
|
||||
Parse._request(
|
||||
|
||||
@@ -645,7 +645,7 @@ describe('google auth adapter', () => {
|
||||
// }
|
||||
// });
|
||||
|
||||
it('(using client id as string) should verify id_token', async () => {
|
||||
it('(using client id as string) should verify id_token (google.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://accounts.google.com',
|
||||
aud: 'secret',
|
||||
@@ -663,7 +663,7 @@ describe('google auth adapter', () => {
|
||||
expect(result).toEqual(fakeClaim);
|
||||
});
|
||||
|
||||
it('(using client id as string) should throw error with with invalid jwt issuer', async () => {
|
||||
it('(using client id as string) should throw error with with invalid jwt issuer (google.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://not.google.com',
|
||||
sub: 'the_user_id',
|
||||
@@ -1466,7 +1466,7 @@ describe('apple signin auth adapter', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should use algorithm from key header to verify id_token', async () => {
|
||||
it('should use algorithm from key header to verify id_token (apple.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://appleid.apple.com',
|
||||
aud: 'secret',
|
||||
@@ -1516,7 +1516,7 @@ describe('apple signin auth adapter', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('(using client id as string) should verify id_token', async () => {
|
||||
it('(using client id as string) should verify id_token (apple.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://appleid.apple.com',
|
||||
aud: 'secret',
|
||||
@@ -1536,7 +1536,7 @@ describe('apple signin auth adapter', () => {
|
||||
expect(result).toEqual(fakeClaim);
|
||||
});
|
||||
|
||||
it('(using client id as array) should verify id_token', async () => {
|
||||
it('(using client id as array) should verify id_token (apple.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://appleid.apple.com',
|
||||
aud: 'secret',
|
||||
@@ -1556,7 +1556,7 @@ describe('apple signin auth adapter', () => {
|
||||
expect(result).toEqual(fakeClaim);
|
||||
});
|
||||
|
||||
it('(using client id as array with multiple items) should verify id_token', async () => {
|
||||
it('(using client id as array with multiple items) should verify id_token (apple.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://appleid.apple.com',
|
||||
aud: 'secret',
|
||||
@@ -1576,7 +1576,7 @@ describe('apple signin auth adapter', () => {
|
||||
expect(result).toEqual(fakeClaim);
|
||||
});
|
||||
|
||||
it('(using client id as string) should throw error with with invalid jwt issuer', async () => {
|
||||
it('(using client id as string) should throw error with with invalid jwt issuer (apple.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://not.apple.com',
|
||||
sub: 'the_user_id',
|
||||
@@ -1629,7 +1629,7 @@ describe('apple signin auth adapter', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('(using client id as string) should throw error with with invalid jwt issuer with token', async () => {
|
||||
it('(using client id as string) should throw error with with invalid jwt issuer with token (apple.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://not.apple.com',
|
||||
sub: 'the_user_id',
|
||||
@@ -1698,7 +1698,7 @@ describe('apple signin auth adapter', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw error with with invalid user id', async () => {
|
||||
it('should throw error with with invalid user id (apple.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://appleid.apple.com',
|
||||
aud: 'invalid_client_id',
|
||||
@@ -2022,7 +2022,7 @@ describe('facebook limited auth adapter', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should use algorithm from key header to verify id_token', async () => {
|
||||
it_id('7bfa55ab-8fd7-4526-992e-6de3df16bf9c')(it)('should use algorithm from key header to verify id_token (facebook.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://www.facebook.com',
|
||||
aud: 'secret',
|
||||
@@ -2072,7 +2072,7 @@ describe('facebook limited auth adapter', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('(using client id as string) should verify id_token', async () => {
|
||||
it_id('4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a')(it)('using client id as string) should verify id_token (facebook.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://www.facebook.com',
|
||||
aud: 'secret',
|
||||
@@ -2092,7 +2092,7 @@ describe('facebook limited auth adapter', () => {
|
||||
expect(result).toEqual(fakeClaim);
|
||||
});
|
||||
|
||||
it('(using client id as array) should verify id_token', async () => {
|
||||
it_id('c521a272-2ac2-4d8b-b5ed-ea250336d8b1')(it)('(using client id as array) should verify id_token (facebook.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://www.facebook.com',
|
||||
aud: 'secret',
|
||||
@@ -2112,7 +2112,7 @@ describe('facebook limited auth adapter', () => {
|
||||
expect(result).toEqual(fakeClaim);
|
||||
});
|
||||
|
||||
it('(using client id as array with multiple items) should verify id_token', async () => {
|
||||
it_id('e3f16404-18e9-4a87-a555-4710cfbdac67')(it)('(using client id as array with multiple items) should verify id_token (facebook.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://www.facebook.com',
|
||||
aud: 'secret',
|
||||
@@ -2132,7 +2132,7 @@ describe('facebook limited auth adapter', () => {
|
||||
expect(result).toEqual(fakeClaim);
|
||||
});
|
||||
|
||||
it('(using client id as string) should throw error with with invalid jwt issuer', async () => {
|
||||
it_id('549c33a1-3a6b-4732-8cf6-8f010ad4569c')(it)('(using client id as string) should throw error with with invalid jwt issuer (facebook.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://not.facebook.com',
|
||||
sub: 'the_user_id',
|
||||
@@ -2263,7 +2263,7 @@ describe('facebook limited auth adapter', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw error with with invalid user id', async () => {
|
||||
it_id('c194d902-e697-46c9-a303-82c2d914473c')(it)('should throw error with with invalid user id (facebook.com)', async () => {
|
||||
const fakeClaim = {
|
||||
iss: 'https://www.facebook.com',
|
||||
aud: 'invalid_client_id',
|
||||
|
||||
+4
-4
@@ -248,7 +248,7 @@ describe('execution', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should start Parse Server', done => {
|
||||
it_id('a0ab74b4-f805-4e03-b31d-b5cd59e64495')(it)('should start Parse Server', done => {
|
||||
const env = { ...process.env };
|
||||
env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
|
||||
childProcess = spawn(
|
||||
@@ -261,7 +261,7 @@ describe('execution', () => {
|
||||
handleError(childProcess, done);
|
||||
});
|
||||
|
||||
it('should start Parse Server with GraphQL', async done => {
|
||||
it_id('d7165081-b133-4cba-901b-19128ce41301')(it)('should start Parse Server with GraphQL', async done => {
|
||||
const env = { ...process.env };
|
||||
env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
|
||||
childProcess = spawn(
|
||||
@@ -287,7 +287,7 @@ describe('execution', () => {
|
||||
handleError(childProcess, done);
|
||||
});
|
||||
|
||||
it('should start Parse Server with GraphQL and Playground', async done => {
|
||||
it_id('2769cdb4-ce8a-484d-8a91-635b5894ba7e')(it)('should start Parse Server with GraphQL and Playground', async done => {
|
||||
const env = { ...process.env };
|
||||
env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
|
||||
childProcess = spawn(
|
||||
@@ -315,7 +315,7 @@ describe('execution', () => {
|
||||
handleError(childProcess, done);
|
||||
});
|
||||
|
||||
it('can start Parse Server with auth via CLI', done => {
|
||||
it_id('23caddd7-bfea-4869-8bd4-0f2cd283c8bd')(it)('can start Parse Server with auth via CLI', done => {
|
||||
const env = { ...process.env };
|
||||
env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
|
||||
childProcess = spawn(
|
||||
|
||||
@@ -6,6 +6,9 @@ const validatorFail = () => {
|
||||
const validatorSuccess = () => {
|
||||
return true;
|
||||
};
|
||||
function testConfig() {
|
||||
return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true });
|
||||
}
|
||||
|
||||
describe('cloud validator', () => {
|
||||
it('complete validator', async done => {
|
||||
@@ -731,6 +734,38 @@ describe('cloud validator', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it_id('893eec0c-41bd-4adf-8f0a-306087ad8d61')(it)('basic beforeSave Parse.Config skipWithMasterKey', async () => {
|
||||
Parse.Cloud.beforeSave(
|
||||
Parse.Config,
|
||||
() => {
|
||||
throw 'beforeSaveFile should have resolved using master key.';
|
||||
},
|
||||
{
|
||||
skipWithMasterKey: true,
|
||||
}
|
||||
);
|
||||
const config = await testConfig();
|
||||
expect(config.get('internal')).toBe('i');
|
||||
expect(config.get('string')).toBe('s');
|
||||
expect(config.get('number')).toBe(12);
|
||||
});
|
||||
|
||||
it_id('91e739a4-6a38-405c-8f83-f36d48220734')(it)('basic afterSave Parse.Config skipWithMasterKey', async () => {
|
||||
Parse.Cloud.afterSave(
|
||||
Parse.Config,
|
||||
() => {
|
||||
throw 'beforeSaveFile should have resolved using master key.';
|
||||
},
|
||||
{
|
||||
skipWithMasterKey: true,
|
||||
}
|
||||
);
|
||||
const config = await testConfig();
|
||||
expect(config.get('internal')).toBe('i');
|
||||
expect(config.get('string')).toBe('s');
|
||||
expect(config.get('number')).toBe(12);
|
||||
});
|
||||
|
||||
it('beforeSave validateMasterKey and skipWithMasterKey fail', async function (done) {
|
||||
Parse.Cloud.beforeSave(
|
||||
'BeforeSave',
|
||||
@@ -1441,7 +1476,7 @@ describe('cloud validator', () => {
|
||||
});
|
||||
|
||||
it('validate afterSaveFile fail', async done => {
|
||||
Parse.Cloud.beforeSave(Parse.File, () => {}, validatorFail);
|
||||
Parse.Cloud.afterSave(Parse.File, () => {}, validatorFail);
|
||||
try {
|
||||
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
|
||||
await file.save({ useMasterKey: true });
|
||||
@@ -1496,6 +1531,42 @@ describe('cloud validator', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it_id('32ca1a99-7f2b-429d-a7cf-62b6661d0af6')(it)('validate beforeSave Parse.Config', async () => {
|
||||
Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorSuccess);
|
||||
const config = await testConfig();
|
||||
expect(config.get('internal')).toBe('i');
|
||||
expect(config.get('string')).toBe('s');
|
||||
expect(config.get('number')).toBe(12);
|
||||
});
|
||||
|
||||
it_id('c84d11e7-d09c-4843-ad98-f671511bf612')(it)('validate beforeSave Parse.Config fail', async () => {
|
||||
Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorFail);
|
||||
try {
|
||||
await testConfig();
|
||||
fail('cloud function should have failed.');
|
||||
} catch (e) {
|
||||
expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
|
||||
}
|
||||
});
|
||||
|
||||
it_id('b18b9a6a-0e35-4b60-9771-30f53501df3c')(it)('validate afterSave Parse.Config', async () => {
|
||||
Parse.Cloud.afterSave(Parse.Config, () => {}, validatorSuccess);
|
||||
const config = await testConfig();
|
||||
expect(config.get('internal')).toBe('i');
|
||||
expect(config.get('string')).toBe('s');
|
||||
expect(config.get('number')).toBe(12);
|
||||
});
|
||||
|
||||
it_id('ef761222-1758-4614-b984-da84d73fc10c')(it)('validate afterSave Parse.Config fail', async () => {
|
||||
Parse.Cloud.afterSave(Parse.Config, () => {}, validatorFail);
|
||||
try {
|
||||
await testConfig();
|
||||
fail('cloud function should have failed.');
|
||||
} catch (e) {
|
||||
expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
|
||||
}
|
||||
});
|
||||
|
||||
it('Should have validator', async done => {
|
||||
Parse.Cloud.define(
|
||||
'myFunction',
|
||||
|
||||
+162
-6
@@ -2317,7 +2317,7 @@ describe('beforeFind hooks', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty where', done => {
|
||||
it_id('6ef0d226-af30-4dfd-8306-972a1b4becd3')(it)('should handle empty where', done => {
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
const otherQuery = new Parse.Query('MyObject');
|
||||
otherQuery.equalTo('some', true);
|
||||
@@ -2919,7 +2919,7 @@ describe('afterFind hooks', () => {
|
||||
}).toThrow('Only the _Session class is allowed for the afterLogout trigger.');
|
||||
});
|
||||
|
||||
it('should skip afterFind hooks for aggregate', done => {
|
||||
it_id('c16159b5-e8ee-42d5-8fe3-e2f7c006881d')(it)('should skip afterFind hooks for aggregate', done => {
|
||||
const hook = {
|
||||
method: function () {
|
||||
return Promise.reject();
|
||||
@@ -2946,7 +2946,7 @@ describe('afterFind hooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should skip afterFind hooks for distinct', done => {
|
||||
it_id('ca55c90d-36db-422c-9060-a30583ce5224')(it)('should skip afterFind hooks for distinct', done => {
|
||||
const hook = {
|
||||
method: function () {
|
||||
return Promise.reject();
|
||||
@@ -3033,7 +3033,7 @@ describe('afterFind hooks', () => {
|
||||
expect(calledAfter).toBe(false);
|
||||
});
|
||||
|
||||
it('should expose context in beforeSave/afterSave via header', async () => {
|
||||
it_id('55ef1741-cf72-4a7c-a029-00cb75f53233')(it)('should expose context in beforeSave/afterSave via header', async () => {
|
||||
let calledBefore = false;
|
||||
let calledAfter = false;
|
||||
Parse.Cloud.beforeSave('TestObject', req => {
|
||||
@@ -3347,7 +3347,7 @@ describe('beforeLogin hook', () => {
|
||||
expect(response).toEqual(error);
|
||||
});
|
||||
|
||||
it('should have expected data in request', async done => {
|
||||
it_id('5656d6d7-65ef-43d1-8ca6-6942ae3614d5')(it)('should have expected data in request in beforeLogin', async done => {
|
||||
Parse.Cloud.beforeLogin(req => {
|
||||
expect(req.object).toBeDefined();
|
||||
expect(req.user).toBeUndefined();
|
||||
@@ -3464,7 +3464,7 @@ describe('afterLogin hook', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should have expected data in request', async done => {
|
||||
it_id('e86155c4-62e1-4c6e-ab4a-9ac6c87c60f2')(it)('should have expected data in request in afterLogin', async done => {
|
||||
Parse.Cloud.afterLogin(req => {
|
||||
expect(req.object).toBeDefined();
|
||||
expect(req.user).toBeDefined();
|
||||
@@ -3921,6 +3921,162 @@ describe('saveFile hooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cloud Config hooks', () => {
|
||||
function testConfig() {
|
||||
return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true });
|
||||
}
|
||||
|
||||
it_id('997fe20a-96f7-454a-a5b0-c155b8d02f05')(it)('beforeSave(Parse.Config) can run hook with new config', async () => {
|
||||
let count = 0;
|
||||
Parse.Cloud.beforeSave(Parse.Config, (req) => {
|
||||
expect(req.object).toBeDefined();
|
||||
expect(req.original).toBeUndefined();
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.headers).toBeDefined();
|
||||
expect(req.ip).toBeDefined();
|
||||
expect(req.installationId).toBeDefined();
|
||||
expect(req.context).toBeDefined();
|
||||
const config = req.object;
|
||||
expect(config.get('internal')).toBe('i');
|
||||
expect(config.get('string')).toBe('s');
|
||||
expect(config.get('number')).toBe(12);
|
||||
count += 1;
|
||||
});
|
||||
await testConfig();
|
||||
const config = await Parse.Config.get({ useMasterKey: true });
|
||||
expect(config.get('internal')).toBe('i');
|
||||
expect(config.get('string')).toBe('s');
|
||||
expect(config.get('number')).toBe(12);
|
||||
expect(count).toBe(1);
|
||||
});
|
||||
|
||||
it_id('06a9b66c-ffb4-43d1-a025-f7d2192500e7')(it)('beforeSave(Parse.Config) can run hook with existing config', async () => {
|
||||
let count = 0;
|
||||
Parse.Cloud.beforeSave(Parse.Config, (req) => {
|
||||
if (count === 0) {
|
||||
expect(req.object.get('number')).toBe(12);
|
||||
expect(req.original).toBeUndefined();
|
||||
}
|
||||
if (count === 1) {
|
||||
expect(req.object.get('number')).toBe(13);
|
||||
expect(req.original.get('number')).toBe(12);
|
||||
}
|
||||
count += 1;
|
||||
});
|
||||
await testConfig();
|
||||
await Parse.Config.save({ number: 13 });
|
||||
expect(count).toBe(2);
|
||||
});
|
||||
|
||||
it_id('ca76de8e-671b-4c2d-9535-bd28a855fa1a')(it)('beforeSave(Parse.Config) should not change config if nothing is returned', async () => {
|
||||
let count = 0;
|
||||
Parse.Cloud.beforeSave(Parse.Config, () => {
|
||||
count += 1;
|
||||
return;
|
||||
});
|
||||
await testConfig();
|
||||
const config = await Parse.Config.get({ useMasterKey: true });
|
||||
expect(config.get('internal')).toBe('i');
|
||||
expect(config.get('string')).toBe('s');
|
||||
expect(config.get('number')).toBe(12);
|
||||
expect(count).toBe(1);
|
||||
});
|
||||
|
||||
it('beforeSave(Parse.Config) throw custom error', async () => {
|
||||
Parse.Cloud.beforeSave(Parse.Config, () => {
|
||||
throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail');
|
||||
});
|
||||
try {
|
||||
await testConfig();
|
||||
fail('error should have thrown');
|
||||
} catch (e) {
|
||||
expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
|
||||
expect(e.message).toBe('It should fail');
|
||||
}
|
||||
});
|
||||
|
||||
it('beforeSave(Parse.Config) throw string error', async () => {
|
||||
Parse.Cloud.beforeSave(Parse.Config, () => {
|
||||
throw 'before save failed';
|
||||
});
|
||||
try {
|
||||
await testConfig();
|
||||
fail('error should have thrown');
|
||||
} catch (e) {
|
||||
expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
|
||||
expect(e.message).toBe('before save failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('beforeSave(Parse.Config) throw empty error', async () => {
|
||||
Parse.Cloud.beforeSave(Parse.Config, () => {
|
||||
throw null;
|
||||
});
|
||||
try {
|
||||
await testConfig();
|
||||
fail('error should have thrown');
|
||||
} catch (e) {
|
||||
expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
|
||||
expect(e.message).toBe('Script failed. Unknown error.');
|
||||
}
|
||||
});
|
||||
|
||||
it_id('3e7a75c0-6c2e-4c7e-b042-6eb5f23acf94')(it)('afterSave(Parse.Config) can run hook with new config', async () => {
|
||||
let count = 0;
|
||||
Parse.Cloud.afterSave(Parse.Config, (req) => {
|
||||
expect(req.object).toBeDefined();
|
||||
expect(req.original).toBeUndefined();
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.headers).toBeDefined();
|
||||
expect(req.ip).toBeDefined();
|
||||
expect(req.installationId).toBeDefined();
|
||||
expect(req.context).toBeDefined();
|
||||
const config = req.object;
|
||||
expect(config.get('internal')).toBe('i');
|
||||
expect(config.get('string')).toBe('s');
|
||||
expect(config.get('number')).toBe(12);
|
||||
count += 1;
|
||||
});
|
||||
await testConfig();
|
||||
const config = await Parse.Config.get({ useMasterKey: true });
|
||||
expect(config.get('internal')).toBe('i');
|
||||
expect(config.get('string')).toBe('s');
|
||||
expect(config.get('number')).toBe(12);
|
||||
expect(count).toBe(1);
|
||||
});
|
||||
|
||||
it_id('5cffb28a-2924-4857-84bb-f5778d80372a')(it)('afterSave(Parse.Config) can run hook with existing config', async () => {
|
||||
let count = 0;
|
||||
Parse.Cloud.afterSave(Parse.Config, (req) => {
|
||||
if (count === 0) {
|
||||
expect(req.object.get('number')).toBe(12);
|
||||
expect(req.original).toBeUndefined();
|
||||
}
|
||||
if (count === 1) {
|
||||
expect(req.object.get('number')).toBe(13);
|
||||
expect(req.original.get('number')).toBe(12);
|
||||
}
|
||||
count += 1;
|
||||
});
|
||||
await testConfig();
|
||||
await Parse.Config.save({ number: 13 });
|
||||
expect(count).toBe(2);
|
||||
});
|
||||
|
||||
it_id('49883992-ce91-4797-85f9-7cce1f819407')(it)('afterSave(Parse.Config) should throw error', async () => {
|
||||
Parse.Cloud.afterSave(Parse.Config, () => {
|
||||
throw new Parse.Error(400, 'It should fail');
|
||||
});
|
||||
try {
|
||||
await testConfig();
|
||||
fail('error should have thrown');
|
||||
} catch (e) {
|
||||
expect(e.code).toBe(400);
|
||||
expect(e.message).toBe('It should fail');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendEmail', () => {
|
||||
it('can send email via Parse.Cloud', async done => {
|
||||
const emailAdapter = {
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('Cloud Code Logger', () => {
|
||||
// Note that helpers takes care of logout.
|
||||
// see helpers.js:afterEach
|
||||
|
||||
it('should expose log to functions', () => {
|
||||
it_id('02d53b97-3ec7-46fb-abb6-176fd6e85590')(it)('should expose log to functions', () => {
|
||||
const spy = spyOn(Config.get('test').loggerController, 'log').and.callThrough();
|
||||
Parse.Cloud.define('loggerTest', req => {
|
||||
req.log.info('logTest', 'info log', { info: 'some log' });
|
||||
@@ -67,7 +67,7 @@ describe('Cloud Code Logger', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('trigger should obfuscate password', done => {
|
||||
it_id('768412f5-d32f-4134-89a6-08949781a6c0')(it)('trigger should obfuscate password', done => {
|
||||
Parse.Cloud.beforeSave(Parse.User, req => {
|
||||
return req.object;
|
||||
});
|
||||
@@ -82,7 +82,7 @@ describe('Cloud Code Logger', () => {
|
||||
.then(null, e => done.fail(e));
|
||||
});
|
||||
|
||||
it('should expose log to trigger', done => {
|
||||
it_id('3c394047-272e-4728-9d02-9eaa660d2ed2')(it)('should expose log to trigger', done => {
|
||||
Parse.Cloud.beforeSave('MyObject', req => {
|
||||
req.log.info('beforeSave MyObject', 'info log', { info: 'some log' });
|
||||
req.log.error('beforeSave MyObject', 'error log', {
|
||||
@@ -120,7 +120,7 @@ describe('Cloud Code Logger', () => {
|
||||
expect(truncatedString.length).toBe(1015); // truncate length + the string '... (truncated)'
|
||||
});
|
||||
|
||||
it('should truncate input and result of long lines', done => {
|
||||
it_id('4a009b1f-9203-49ca-8d48-5b45f4eedbdf')(it)('should truncate input and result of long lines', done => {
|
||||
const longString = fs.readFileSync(loremFile, 'utf8');
|
||||
Parse.Cloud.define('aFunction', req => {
|
||||
return req.params;
|
||||
@@ -138,7 +138,7 @@ describe('Cloud Code Logger', () => {
|
||||
.then(null, e => done.fail(e));
|
||||
});
|
||||
|
||||
it('should log an afterSave', done => {
|
||||
it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)('should log an afterSave', done => {
|
||||
Parse.Cloud.afterSave('MyObject', () => {});
|
||||
new Parse.Object('MyObject')
|
||||
.save()
|
||||
@@ -151,7 +151,7 @@ describe('Cloud Code Logger', () => {
|
||||
.then(null, e => done.fail(e));
|
||||
});
|
||||
|
||||
it('should log a denied beforeSave', done => {
|
||||
it_id('ec13a296-f8b1-4fc6-985a-3593462edd9c')(it)('should log a denied beforeSave', done => {
|
||||
Parse.Cloud.beforeSave('MyObject', () => {
|
||||
throw 'uh oh!';
|
||||
});
|
||||
@@ -174,7 +174,7 @@ describe('Cloud Code Logger', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should log cloud function success', done => {
|
||||
it_id('3e0caa45-60d6-41af-829a-fd389710c132')(it)('should log cloud function success', done => {
|
||||
Parse.Cloud.define('aFunction', () => {
|
||||
return 'it worked!';
|
||||
});
|
||||
@@ -189,7 +189,7 @@ describe('Cloud Code Logger', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should log cloud function execution using the custom log level', async done => {
|
||||
it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)('should log cloud function execution using the custom log level', async done => {
|
||||
Parse.Cloud.define('aFunction', () => {
|
||||
return 'it worked!';
|
||||
});
|
||||
@@ -260,7 +260,7 @@ describe('Cloud Code Logger', () => {
|
||||
expect(calls).toEqual({ beforeSave: 'warn', afterSave: undefined });
|
||||
});
|
||||
|
||||
it('should log cloud function failure', done => {
|
||||
it_id('97e0eafa-cde6-4a9a-9e53-7db98bacbc62')(it)('should log cloud function failure', done => {
|
||||
Parse.Cloud.define('aFunction', () => {
|
||||
throw 'it failed!';
|
||||
});
|
||||
@@ -311,7 +311,7 @@ describe('Cloud Code Logger', () => {
|
||||
.then(null, e => done.fail(JSON.stringify(e)));
|
||||
}).pend('needs more work.....');
|
||||
|
||||
it('cloud function should obfuscate password', done => {
|
||||
it_id('b86e8168-8370-4730-a4ba-24ca3016ad66')(it)('cloud function should obfuscate password', done => {
|
||||
Parse.Cloud.define('testFunction', () => {
|
||||
return 'verify code success';
|
||||
});
|
||||
|
||||
@@ -642,7 +642,8 @@ describe('DefinedSchemas', () => {
|
||||
|
||||
expect(logger.error).toHaveBeenCalledWith(`Failed to run migrations: ${error.toString()}`);
|
||||
});
|
||||
it('should perform migration in parallel without failing', async () => {
|
||||
|
||||
it_id('a18bf4f2-25c8-4de3-b986-19cb1ab163b8')(it)('should perform migration in parallel without failing', async () => {
|
||||
const server = await reconfigureServer();
|
||||
const logger = require('../lib/logger').logger;
|
||||
spyOn(logger, 'error').and.callThrough();
|
||||
|
||||
@@ -104,7 +104,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('if user clicks on the email verify link before email verification token expiration then show the verify email success page', done => {
|
||||
it_id('f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc')(it)('if user clicks on the email verify link before email verification token expiration then show the verify email success page', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
@@ -146,7 +146,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('if user clicks on the email verify link before email verification token expiration then emailVerified should be true', done => {
|
||||
it_id('94956799-c85e-4297-b879-e2d1f985394c')(it)('if user clicks on the email verify link before email verification token expiration then emailVerified should be true', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
@@ -194,7 +194,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('if user clicks on the email verify link before email verification token expiration then user should be able to login', done => {
|
||||
it_id('25f3f895-c987-431c-9841-17cb6aaf18b5')(it)('if user clicks on the email verify link before email verification token expiration then user should be able to login', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
@@ -242,7 +242,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', done => {
|
||||
it_id('c6a3e188-9065-4f50-842d-454d1e82f289')(it)('sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
@@ -292,7 +292,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('can conditionally send emails', async () => {
|
||||
it_id('9365c53c-b8b4-41f7-a3c1-77882f76a89c')(it)('can conditionally send emails', async () => {
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
@@ -351,7 +351,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
expect(verifySpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can conditionally send emails and allow conditional login', async () => {
|
||||
it_id('b3549300-bed7-4a5e-bed5-792dbfead960')(it)('can conditionally send emails and allow conditional login', async () => {
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
@@ -397,7 +397,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
expect(verifySpy).toHaveBeenCalledTimes(5);
|
||||
});
|
||||
|
||||
it('can conditionally send user email verification', async () => {
|
||||
it_id('d812de87-33d1-495e-a6e8-3485f6dc3589')(it)('can conditionally send user email verification', async () => {
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: () => {},
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
@@ -431,7 +431,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
expect(emailSpy).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('provides full user object in email verification function on email and username change', async () => {
|
||||
it_id('d98babc1-feb8-4b5e-916c-57dc0a6ed9fb')(it)('provides full user object in email verification function on email and username change', async () => {
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: () => {},
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
@@ -467,7 +467,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
await user.save(null, { useMasterKey: true });
|
||||
});
|
||||
|
||||
it('beforeSave options do not change existing behaviour', async () => {
|
||||
it_id('a8c1f820-822f-4a37-9d08-a968cac8369d')(it)('beforeSave options do not change existing behaviour', async () => {
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
@@ -509,7 +509,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
expect(emailSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', done => {
|
||||
it_id('36d277eb-ec7c-4a39-9108-435b68228741')(it)('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
@@ -567,7 +567,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success', done => {
|
||||
it_id('4f444704-ec4b-4dff-b947-614b1c6971c4')(it)('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
@@ -679,7 +679,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', done => {
|
||||
it_id('b6c87f35-d887-477d-bc86-a9217a424f53')(it)('setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', done => {
|
||||
const user = new Parse.User();
|
||||
let userBeforeEmailReset;
|
||||
|
||||
@@ -755,7 +755,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should send a new verification email when a resend is requested and the user is UNVERIFIED', done => {
|
||||
it_id('28f2140d-48bd-44ac-a141-ba60ea8d9713')(it)('should send a new verification email when a resend is requested and the user is UNVERIFIED', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
let sendVerificationEmailCallCount = 0;
|
||||
@@ -916,7 +916,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should match codes with emailVerifyTokenReuseIfValid', async done => {
|
||||
it_id('0e66b7f6-2c07-4117-a8b9-605aa31a1e29')(it)('should match codes with emailVerifyTokenReuseIfValid', async done => {
|
||||
let sendEmailOptions;
|
||||
let sendVerificationEmailCallCount = 0;
|
||||
const emailAdapter = {
|
||||
@@ -982,7 +982,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should not send a new verification email when a resend is requested and the user is VERIFIED', done => {
|
||||
it_id('1ed9a6c2-bebc-4813-af30-4f4a212544c2')(it)('should not send a new verification email when a resend is requested and the user is VERIFIED', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
let sendVerificationEmailCallCount = 0;
|
||||
@@ -1223,7 +1223,7 @@ describe('Email Verification Token Expiration: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('emailVerified should be set to false after changing from an already verified email', done => {
|
||||
it_id('b082d387-4974-4d45-a0d9-0c85ca2d7cbf')(it)('emailVerified should be set to false after changing from an already verified email', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
|
||||
@@ -21,11 +21,14 @@ const mockAdapter = {
|
||||
|
||||
// Small additional tests to improve overall coverage
|
||||
describe('FilesController', () => {
|
||||
it('should properly expand objects', done => {
|
||||
it('should properly expand objects with sync getFileLocation', async () => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
gridFSAdapter.getFileLocation = (config, filename) => {
|
||||
return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
|
||||
}
|
||||
const filesController = new FilesController(gridFSAdapter);
|
||||
const result = filesController.expandFilesInObject(config, function () {});
|
||||
const result = await filesController.expandFilesInObject(config, function () { });
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
@@ -37,12 +40,69 @@ describe('FilesController', () => {
|
||||
const anObject = {
|
||||
aFile: fullFile,
|
||||
};
|
||||
filesController.expandFilesInObject(config, anObject);
|
||||
await filesController.expandFilesInObject(config, anObject);
|
||||
expect(anObject.aFile.url).toEqual('http://an.url');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should properly expand objects with async getFileLocation', async () => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
gridFSAdapter.getFileLocation = async (config, filename) => {
|
||||
await Promise.resolve();
|
||||
return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
|
||||
}
|
||||
const filesController = new FilesController(gridFSAdapter);
|
||||
const result = await filesController.expandFilesInObject(config, function () { });
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
const fullFile = {
|
||||
type: '__type',
|
||||
url: 'http://an.url',
|
||||
};
|
||||
|
||||
const anObject = {
|
||||
aFile: fullFile,
|
||||
};
|
||||
await filesController.expandFilesInObject(config, anObject);
|
||||
expect(anObject.aFile.url).toEqual('http://an.url');
|
||||
});
|
||||
|
||||
it('should call getFileLocation when config.fileKey is undefined', async () => {
|
||||
const config = {};
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
|
||||
const fullFile = {
|
||||
name: 'mock-name',
|
||||
__type: 'File',
|
||||
};
|
||||
gridFSAdapter.getFileLocation = jasmine.createSpy('getFileLocation').and.returnValue(Promise.resolve('mock-url'));
|
||||
const filesController = new FilesController(gridFSAdapter);
|
||||
|
||||
const anObject = { aFile: fullFile };
|
||||
await filesController.expandFilesInObject(config, anObject);
|
||||
expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name);
|
||||
expect(anObject.aFile.url).toEqual('mock-url');
|
||||
});
|
||||
|
||||
it('should call getFileLocation when config.fileKey is defined', async () => {
|
||||
const config = { fileKey: 'mock-key' };
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
|
||||
const fullFile = {
|
||||
name: 'mock-name',
|
||||
__type: 'File',
|
||||
};
|
||||
gridFSAdapter.getFileLocation = jasmine.createSpy('getFileLocation').and.returnValue(Promise.resolve('mock-url'));
|
||||
const filesController = new FilesController(gridFSAdapter);
|
||||
|
||||
const anObject = { aFile: fullFile };
|
||||
await filesController.expandFilesInObject(config, anObject);
|
||||
expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name);
|
||||
expect(anObject.aFile.url).toEqual('mock-url');
|
||||
});
|
||||
|
||||
|
||||
it_only_db('mongo')('should pass databaseOptions to GridFSBucketAdapter', async () => {
|
||||
await reconfigureServer({
|
||||
databaseURI: 'mongodb://localhost:27017/parse',
|
||||
@@ -101,7 +161,7 @@ describe('FilesController', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should add a unique hash to the file name when the preserveFileName option is false', done => {
|
||||
it('should add a unique hash to the file name when the preserveFileName option is false', async () => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
spyOn(gridFSAdapter, 'createFile');
|
||||
@@ -112,17 +172,15 @@ describe('FilesController', () => {
|
||||
preserveFileName: false,
|
||||
});
|
||||
|
||||
filesController.createFile(config, fileName);
|
||||
await filesController.createFile(config, fileName);
|
||||
|
||||
expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
|
||||
expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toMatch(
|
||||
`^.{32}_${regexEscapedFileName}$`
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should not add a unique hash to the file name when the preserveFileName option is true', done => {
|
||||
it('should not add a unique hash to the file name when the preserveFileName option is true', async () => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
spyOn(gridFSAdapter, 'createFile');
|
||||
@@ -132,12 +190,10 @@ describe('FilesController', () => {
|
||||
preserveFileName: true,
|
||||
});
|
||||
|
||||
filesController.createFile(config, fileName);
|
||||
await filesController.createFile(config, fileName);
|
||||
|
||||
expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
|
||||
expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should handle adapter without getMetadata', async () => {
|
||||
|
||||
@@ -45,8 +45,12 @@ describe('Idempotency', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
|
||||
});
|
||||
|
||||
// Tests
|
||||
it('should enforce idempotency for cloud code function', async () => {
|
||||
it_id('e25955fd-92eb-4b22-b8b7-38980e5cb223')(it)('should enforce idempotency for cloud code function', async () => {
|
||||
let counter = 0;
|
||||
Parse.Cloud.define('myFunction', () => {
|
||||
counter++;
|
||||
@@ -69,7 +73,7 @@ describe('Idempotency', () => {
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
it('should delete request entry after TTL', async () => {
|
||||
it_id('be2fbe16-8178-485e-9a12-6fb541096480')(it)('should delete request entry after TTL', async () => {
|
||||
let counter = 0;
|
||||
Parse.Cloud.define('myFunction', () => {
|
||||
counter++;
|
||||
@@ -119,7 +123,7 @@ describe('Idempotency', () => {
|
||||
}
|
||||
);
|
||||
|
||||
it('should enforce idempotency for cloud code jobs', async () => {
|
||||
it_id('e976d0cc-a57f-45d4-9472-b9b052db6490')(it)('should enforce idempotency for cloud code jobs', async () => {
|
||||
let counter = 0;
|
||||
Parse.Cloud.job('myJob', () => {
|
||||
counter++;
|
||||
@@ -141,7 +145,7 @@ describe('Idempotency', () => {
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
it('should enforce idempotency for class object creation', async () => {
|
||||
it_id('7c84a3d4-e1b6-4a0d-99f1-af3cf1a6b3d8')(it)('should enforce idempotency for class object creation', async () => {
|
||||
let counter = 0;
|
||||
Parse.Cloud.afterSave('MyClass', () => {
|
||||
counter++;
|
||||
@@ -163,7 +167,7 @@ describe('Idempotency', () => {
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
it('should enforce idempotency for user object creation', async () => {
|
||||
it_id('a030f2dd-5d21-46ac-b53d-9d714f35d72a')(it)('should enforce idempotency for user object creation', async () => {
|
||||
let counter = 0;
|
||||
Parse.Cloud.afterSave('_User', () => {
|
||||
counter++;
|
||||
@@ -189,7 +193,7 @@ describe('Idempotency', () => {
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
it('should enforce idempotency for installation object creation', async () => {
|
||||
it_id('064c469b-091c-4ba9-9043-be461f26a3eb')(it)('should enforce idempotency for installation object creation', async () => {
|
||||
let counter = 0;
|
||||
Parse.Cloud.afterSave('_Installation', () => {
|
||||
counter++;
|
||||
@@ -215,7 +219,7 @@ describe('Idempotency', () => {
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
it('should not interfere with calls of different request ID', async () => {
|
||||
it_id('f11670b6-fa9c-4f21-a268-ae4b6bbff7fd')(it)('should not interfere with calls of different request ID', async () => {
|
||||
let counter = 0;
|
||||
Parse.Cloud.afterSave('MyClass', () => {
|
||||
counter++;
|
||||
@@ -236,7 +240,7 @@ describe('Idempotency', () => {
|
||||
expect(counter).toBe(100);
|
||||
});
|
||||
|
||||
it('should re-throw any other error unchanged when writing request entry fails for any other reason', async () => {
|
||||
it_id('0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777')(it)('should re-throw any other error unchanged when writing request entry fails for any other reason', async () => {
|
||||
spyOn(rest, 'create').and.rejectWith(new Parse.Error(0, 'some other error'));
|
||||
Parse.Cloud.define('myFunction', () => {});
|
||||
const params = {
|
||||
|
||||
@@ -75,7 +75,7 @@ describe_only(() => {
|
||||
/**
|
||||
* Verifies simple passwords in GET login requests with special characters are scrubbed from the verbose log
|
||||
*/
|
||||
it('does scrub simple passwords on GET login', done => {
|
||||
it_id('e36d6141-2a20-41d0-85fc-d1534c3e4bae')(it)('does scrub simple passwords on GET login', done => {
|
||||
reconfigureServer({
|
||||
verbose: true,
|
||||
}).then(function () {
|
||||
@@ -105,7 +105,7 @@ describe_only(() => {
|
||||
/**
|
||||
* Verifies complex passwords in GET login requests with special characters are scrubbed from the verbose log
|
||||
*/
|
||||
it('does scrub complex passwords on GET login', done => {
|
||||
it_id('24b277c5-250f-4a35-a449-2c8c519d4c03')(it)('does scrub complex passwords on GET login', done => {
|
||||
reconfigureServer({
|
||||
verbose: true,
|
||||
})
|
||||
@@ -139,7 +139,7 @@ describe_only(() => {
|
||||
/**
|
||||
* Verifies fields in POST login requests are NOT present in the verbose log
|
||||
*/
|
||||
it('does not have password field in POST login', done => {
|
||||
it_id('33143ec9-b32d-467c-ba65-ff2bbefdaadd')(it)('does not have password field in POST login', done => {
|
||||
reconfigureServer({
|
||||
verbose: true,
|
||||
}).then(function () {
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('middlewares', () => {
|
||||
AppCache.del(fakeReq.body._ApplicationId);
|
||||
});
|
||||
|
||||
it('should use _ContentType if provided', done => {
|
||||
it_id('4cc18d90-1763-4725-97fa-f63fb4692fc4')(it)('should use _ContentType if provided', done => {
|
||||
AppCachePut(fakeReq.body._ApplicationId, {
|
||||
masterKeyIps: ['127.0.0.1'],
|
||||
});
|
||||
@@ -128,7 +128,7 @@ describe('middlewares', () => {
|
||||
const otherKeys = BodyKeys.filter(
|
||||
otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey'
|
||||
);
|
||||
it(`it should pull ${bodyKey} into req.info`, done => {
|
||||
it_id('f9abd7ac-b1f4-4607-b9b0-365ff0559d84')(it)(`it should pull ${bodyKey} into req.info`, done => {
|
||||
AppCachePut(fakeReq.body._ApplicationId, {
|
||||
masterKeyIps: ['0.0.0.0/0'],
|
||||
});
|
||||
@@ -147,7 +147,7 @@ describe('middlewares', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not succeed and log if the ip does not belong to masterKeyIps list', async () => {
|
||||
it_id('4a0bce41-c536-4482-a873-12ed023380e2')(it)('should not succeed and log if the ip does not belong to masterKeyIps list', async () => {
|
||||
const logger = require('../lib/logger').logger;
|
||||
spyOn(logger, 'error').and.callFake(() => {});
|
||||
AppCachePut(fakeReq.body._ApplicationId, {
|
||||
@@ -197,7 +197,7 @@ describe('middlewares', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should succeed if the ip does belong to masterKeyIps list', async () => {
|
||||
it_id('2f7fadec-a87c-4626-90d1-65c75653aea9')(it)('should succeed if the ip does belong to masterKeyIps list', async () => {
|
||||
AppCachePut(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['10.0.0.1'],
|
||||
@@ -208,7 +208,7 @@ describe('middlewares', () => {
|
||||
expect(fakeReq.auth.isMaster).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow any ip to use masterKey if masterKeyIps is empty', async () => {
|
||||
it_id('2b251fd4-d43c-48f4-ada9-c8458e40c12a')(it)('should allow any ip to use masterKey if masterKeyIps is empty', async () => {
|
||||
AppCachePut(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
masterKeyIps: ['0.0.0.0/0'],
|
||||
|
||||
@@ -738,7 +738,7 @@ describe('Pages Router', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('localizes end-to-end for verify email: success', async () => {
|
||||
it_id('2845c2ea-23ba-45d2-a33f-63181d419bca')(it)('localizes end-to-end for verify email: success', async () => {
|
||||
await reconfigureServer(config);
|
||||
const sendVerificationEmail = spyOn(
|
||||
config.emailAdapter,
|
||||
@@ -767,7 +767,7 @@ describe('Pages Router', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('localizes end-to-end for verify email: invalid verification link - link send success', async () => {
|
||||
it_id('f2272b94-b4ac-474f-8e47-1ca74de136f5')(it)('localizes end-to-end for verify email: invalid verification link - link send success', async () => {
|
||||
await reconfigureServer(config);
|
||||
const sendVerificationEmail = spyOn(
|
||||
config.emailAdapter,
|
||||
@@ -821,7 +821,7 @@ describe('Pages Router', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('localizes end-to-end for verify email: invalid verification link - link send fail', async () => {
|
||||
it_id('1d46d36a-e455-4ae7-8717-e0d286e95f02')(it)('localizes end-to-end for verify email: invalid verification link - link send fail', async () => {
|
||||
await reconfigureServer(config);
|
||||
const sendVerificationEmail = spyOn(
|
||||
config.emailAdapter,
|
||||
@@ -1183,7 +1183,7 @@ describe('Pages Router', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('email verification works with custom endpoint', async () => {
|
||||
it_id('81c1c28e-5dfd-4ffb-a09b-283156c08483')(it)('email verification works with custom endpoint', async () => {
|
||||
config.pages.pagesEndpoint = 'customEndpoint';
|
||||
await reconfigureServer(config);
|
||||
const sendVerificationEmail = spyOn(
|
||||
|
||||
@@ -111,7 +111,7 @@ const setup = function () {
|
||||
};
|
||||
|
||||
describe('Parse.Push', () => {
|
||||
it('should properly send push', async () => {
|
||||
it_id('d1e591c4-2b21-466b-9ee2-5be467b6b771')(it)('should properly send push', async () => {
|
||||
const { sendToInstallationSpy } = await setup();
|
||||
const pushStatusId = await Parse.Push.send({
|
||||
where: {
|
||||
@@ -126,7 +126,7 @@ describe('Parse.Push', () => {
|
||||
expect(sendToInstallationSpy.calls.count()).toEqual(10);
|
||||
});
|
||||
|
||||
it('should properly send push with lowercaseIncrement', async () => {
|
||||
it_id('2a58e3c7-b6f3-4261-a384-6c893b2ac3f3')(it)('should properly send push with lowercaseIncrement', async () => {
|
||||
await setup();
|
||||
const pushStatusId = await Parse.Push.send({
|
||||
where: {
|
||||
@@ -140,7 +140,7 @@ describe('Parse.Push', () => {
|
||||
await pushCompleted(pushStatusId);
|
||||
});
|
||||
|
||||
it('should not allow clients to query _PushStatus', async () => {
|
||||
it_id('e21780b6-2cdd-467e-8013-81030f3288e9')(it)('should not allow clients to query _PushStatus', async () => {
|
||||
await setup();
|
||||
const pushStatusId = await Parse.Push.send({
|
||||
where: {
|
||||
@@ -166,7 +166,7 @@ describe('Parse.Push', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow master key to query _PushStatus', async () => {
|
||||
it_id('924cf5f5-f684-4925-978a-e52c0c457366')(it)('should allow master key to query _PushStatus', async () => {
|
||||
await setup();
|
||||
const pushStatusId = await Parse.Push.send({
|
||||
where: {
|
||||
|
||||
@@ -163,7 +163,7 @@ describe('miscellaneous', function () {
|
||||
expect(numCreated).toBe(1);
|
||||
});
|
||||
|
||||
it('ensure that if people already have duplicate users, they can still sign up new users', async done => {
|
||||
it_id('be1b9ac7-5e5f-4e91-b044-2bd8fb7622ad')(it)('ensure that if people already have duplicate users, they can still sign up new users', async done => {
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (e) {
|
||||
@@ -209,7 +209,7 @@ describe('miscellaneous', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('ensure that if people already have duplicate emails, they can still sign up new users', done => {
|
||||
it_id('d00f907e-41b9-40f6-8168-63e832199a8c')(it)('ensure that if people already have duplicate emails, they can still sign up new users', done => {
|
||||
const config = Config.get('test');
|
||||
// Remove existing data to clear out unique index
|
||||
TestUtils.destroyAllDataPermanently()
|
||||
@@ -289,7 +289,7 @@ describe('miscellaneous', function () {
|
||||
}, fail);
|
||||
});
|
||||
|
||||
it('increment with a user object', function (done) {
|
||||
it_id('33db6efe-7c02-496c-8595-0ef627a94103')(it)('increment with a user object', function (done) {
|
||||
createTestUser()
|
||||
.then(user => {
|
||||
user.increment('foo');
|
||||
@@ -317,7 +317,7 @@ describe('miscellaneous', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('save various data types', function (done) {
|
||||
it_id('bef99522-bcfd-4f79-ba9e-3c3845550401')(it)('save various data types', function (done) {
|
||||
const obj = new TestObject();
|
||||
obj.set('date', new Date());
|
||||
obj.set('array', [1, 2, 3]);
|
||||
@@ -951,7 +951,7 @@ describe('miscellaneous', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('return the updated fields on PUT', async () => {
|
||||
it_id('e9e718a9-4465-4158-b13e-f146855a8892')(it)('return the updated fields on PUT', async () => {
|
||||
const obj = new Parse.Object('GameScore');
|
||||
const pointer = new Parse.Object('Child');
|
||||
await pointer.save();
|
||||
@@ -1023,7 +1023,7 @@ describe('miscellaneous', function () {
|
||||
expect(body.updatedAt).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should response should not change with triggers', async () => {
|
||||
it_id('ea358b59-03c0-45c9-abc7-1fdd67573029')(it)('should response should not change with triggers', async () => {
|
||||
const obj = new Parse.Object('GameScore');
|
||||
const pointer = new Parse.Object('Child');
|
||||
Parse.Cloud.beforeSave('GameScore', request => {
|
||||
@@ -1491,7 +1491,7 @@ describe('miscellaneous', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('properly returns incremented values (#1554)', done => {
|
||||
it_id('b2cd9cf2-13fa-4acd-aaa9-6f81fc1858db')(it)('properly returns incremented values (#1554)', done => {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
@@ -1680,7 +1680,7 @@ describe('miscellaneous', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('purge all objects in class', done => {
|
||||
it_id('8f99ee20-3da7-45ec-b867-ea0eb87524a9')(it)('purge all objects in class', done => {
|
||||
const object = new Parse.Object('TestObject');
|
||||
object.set('foo', 'bar');
|
||||
const object2 = new Parse.Object('TestObject');
|
||||
|
||||
+11
-11
@@ -93,7 +93,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('geo line', async done => {
|
||||
it_id('bbd9e2f6-7f61-458f-98f2-4a563586cd8d')(it)('geo line', async done => {
|
||||
const line = [];
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
const obj = new TestObject();
|
||||
@@ -143,7 +143,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('geo max distance medium', async () => {
|
||||
it_id('e1e86b38-b8a4-4109-8330-a324fe628e0c')(it)('geo max distance medium', async () => {
|
||||
const objects = [];
|
||||
[0, 1, 2].map(function (i) {
|
||||
const obj = new TestObject();
|
||||
@@ -207,7 +207,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('geo max distance in km california', async () => {
|
||||
it_id('05f1a454-56b1-4f2e-908e-408a9222cbae')(it)('geo max distance in km california', async () => {
|
||||
await makeSomeGeoPoints();
|
||||
const sfo = new Parse.GeoPoint(37.6189722, -122.3748889);
|
||||
const query = new Parse.Query(TestObject);
|
||||
@@ -246,7 +246,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
equal(results.length, 3);
|
||||
});
|
||||
|
||||
it('geo max distance in miles california', async () => {
|
||||
it_id('9ee376ad-dd6c-4c17-ad28-c7899a4411f1')(it)('geo max distance in miles california', async () => {
|
||||
await makeSomeGeoPoints();
|
||||
const sfo = new Parse.GeoPoint(37.6189722, -122.3748889);
|
||||
const query = new Parse.Query(TestObject);
|
||||
@@ -276,7 +276,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
equal(results.length, 0);
|
||||
});
|
||||
|
||||
it('returns nearest location', async () => {
|
||||
it_id('9e35a89e-bc2c-4ec5-b25a-8d1890a55233')(it)('returns nearest location', async () => {
|
||||
await makeSomeGeoPoints();
|
||||
const sfo = new Parse.GeoPoint(37.6189722, -122.3748889);
|
||||
const query = new Parse.Query(TestObject);
|
||||
@@ -286,7 +286,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
equal(results[1].get('name'), 'Sacramento');
|
||||
});
|
||||
|
||||
it('works with geobox queries', done => {
|
||||
it_id('6df434b0-142d-4302-bbc6-a6ec5a9d9c68')(it)('works with geobox queries', done => {
|
||||
const inbound = new Parse.GeoPoint(1.5, 1.5);
|
||||
const onbound = new Parse.GeoPoint(10, 10);
|
||||
const outbound = new Parse.GeoPoint(20, 20);
|
||||
@@ -356,7 +356,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('supports withinPolygon open path', done => {
|
||||
it_id('d9fbc5c6-f767-47d6-bb44-3858eb9df15a')(it)('supports withinPolygon open path', done => {
|
||||
const inbound = new Parse.GeoPoint(1.5, 1.5);
|
||||
const onbound = new Parse.GeoPoint(10, 10);
|
||||
const outbound = new Parse.GeoPoint(20, 20);
|
||||
@@ -394,7 +394,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
}, done.fail);
|
||||
});
|
||||
|
||||
it('supports withinPolygon closed path', done => {
|
||||
it_id('3ec537bd-839a-4c93-a48b-b4a249820074')(it)('supports withinPolygon closed path', done => {
|
||||
const inbound = new Parse.GeoPoint(1.5, 1.5);
|
||||
const onbound = new Parse.GeoPoint(10, 10);
|
||||
const outbound = new Parse.GeoPoint(20, 20);
|
||||
@@ -433,7 +433,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
}, done.fail);
|
||||
});
|
||||
|
||||
it('supports withinPolygon Polygon object', done => {
|
||||
it_id('0a248e11-3598-480a-9ab5-8a0b259258e4')(it)('supports withinPolygon Polygon object', done => {
|
||||
const inbound = new Parse.GeoPoint(1.5, 1.5);
|
||||
const onbound = new Parse.GeoPoint(10, 10);
|
||||
const outbound = new Parse.GeoPoint(20, 20);
|
||||
@@ -752,7 +752,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
equal(count, 1);
|
||||
});
|
||||
|
||||
it('withinKilometers complex supports count', async () => {
|
||||
it_id('0b073d31-0d41-41e7-bd60-f636ffb759dc')(it)('withinKilometers complex supports count', async () => {
|
||||
const inside = new Parse.GeoPoint(10, 10);
|
||||
const middle = new Parse.GeoPoint(20, 20);
|
||||
const outside = new Parse.GeoPoint(30, 30);
|
||||
@@ -770,7 +770,7 @@ describe('Parse.GeoPoint testing', () => {
|
||||
equal(count, 2);
|
||||
});
|
||||
|
||||
it('fails to fetch geopoints that are specifically not at (0,0)', async () => {
|
||||
it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)('fails to fetch geopoints that are specifically not at (0,0)', async () => {
|
||||
const tmp = new TestObject({
|
||||
location: new Parse.GeoPoint({ latitude: 0, longitude: 0 }),
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('a GlobalConfig', () => {
|
||||
},
|
||||
query,
|
||||
{
|
||||
params: { companies: ['US', 'DK'], internalParam: 'internal' },
|
||||
params: { companies: ['US', 'DK'], counter: 20, internalParam: 'internal' },
|
||||
masterKeyOnly: { internalParam: true },
|
||||
}
|
||||
)
|
||||
@@ -114,6 +114,34 @@ describe('a GlobalConfig', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_only_db('mongo')('can addUnique', async () => {
|
||||
await Parse.Config.save({ companies: { __op: 'AddUnique', objects: ['PA', 'RS', 'E'] } });
|
||||
const config = await Parse.Config.get();
|
||||
const companies = config.get('companies');
|
||||
expect(companies).toEqual(['US', 'DK', 'PA', 'RS', 'E']);
|
||||
});
|
||||
|
||||
it_only_db('mongo')('can add to array', async () => {
|
||||
await Parse.Config.save({ companies: { __op: 'Add', objects: ['PA'] } });
|
||||
const config = await Parse.Config.get();
|
||||
const companies = config.get('companies');
|
||||
expect(companies).toEqual(['US', 'DK', 'PA']);
|
||||
});
|
||||
|
||||
it_only_db('mongo')('can remove from array', async () => {
|
||||
await Parse.Config.save({ companies: { __op: 'Remove', objects: ['US'] } });
|
||||
const config = await Parse.Config.get();
|
||||
const companies = config.get('companies');
|
||||
expect(companies).toEqual(['DK']);
|
||||
});
|
||||
|
||||
it('can increment', async () => {
|
||||
await Parse.Config.save({ counter: { __op: 'Increment', amount: 49 } });
|
||||
const config = await Parse.Config.get();
|
||||
const counter = config.get('counter');
|
||||
expect(counter).toEqual(69);
|
||||
});
|
||||
|
||||
it('can add and retrive files', done => {
|
||||
request({
|
||||
method: 'PUT',
|
||||
@@ -157,7 +185,7 @@ describe('a GlobalConfig', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('properly handles delete op', done => {
|
||||
it_id('5ebbd0cf-d1a5-49d9-aac7-5216abc5cb62')(it)('properly handles delete op', done => {
|
||||
request({
|
||||
method: 'PUT',
|
||||
url: 'http://localhost:8378/1/config',
|
||||
@@ -165,6 +193,7 @@ describe('a GlobalConfig', () => {
|
||||
body: {
|
||||
params: {
|
||||
companies: { __op: 'Delete' },
|
||||
counter: { __op: 'Delete' },
|
||||
internalParam: { __op: 'Delete' },
|
||||
foo: 'bar',
|
||||
},
|
||||
@@ -183,6 +212,7 @@ describe('a GlobalConfig', () => {
|
||||
try {
|
||||
expect(response.status).toEqual(200);
|
||||
expect(body.params.companies).toBeUndefined();
|
||||
expect(body.params.counter).toBeUndefined();
|
||||
expect(body.params.foo).toBe('bar');
|
||||
expect(Object.keys(body.params).length).toBe(1);
|
||||
} catch (e) {
|
||||
|
||||
@@ -500,7 +500,7 @@ describe('ParseGraphQLSchema', () => {
|
||||
});
|
||||
});
|
||||
describe('alias', () => {
|
||||
it('Should be able to define alias for get and find query', async () => {
|
||||
it_id('45282d26-f4c7-4d2d-a7b6-cd8741d5322f')(it)('Should be able to define alias for get and find query', async () => {
|
||||
const parseGraphQLSchema = new ParseGraphQLSchema({
|
||||
databaseController,
|
||||
parseGraphQLController,
|
||||
@@ -535,7 +535,7 @@ describe('ParseGraphQLSchema', () => {
|
||||
expect(Object.keys(queries1)).toContain('precious_data');
|
||||
});
|
||||
|
||||
it('Should be able to define alias for mutation', async () => {
|
||||
it_id('f04b46e3-a25d-401d-a315-3298cfee1df8')(it)('Should be able to define alias for mutation', async () => {
|
||||
const parseGraphQLSchema = new ParseGraphQLSchema({
|
||||
databaseController,
|
||||
parseGraphQLController,
|
||||
|
||||
@@ -130,7 +130,7 @@ describe('ParseGraphQLServer', () => {
|
||||
set: () => {},
|
||||
};
|
||||
|
||||
it("should return schema and context with req's info, config and auth", async () => {
|
||||
it_id('0696675e-060f-414f-bc77-9d57f31807f5')(it)('should return schema and context with req\'s info, config and auth', async () => {
|
||||
const options = await parseGraphQLServer._getGraphQLOptions();
|
||||
expect(options.schema).toEqual(parseGraphQLServer.parseGraphQLSchema.graphQLSchema);
|
||||
const contextResponse = await options.context({ req, res });
|
||||
@@ -1395,7 +1395,7 @@ describe('ParseGraphQLServer', () => {
|
||||
await resetGraphQLCache();
|
||||
});
|
||||
|
||||
it('should only include types in the enabledForClasses list', async () => {
|
||||
it_id('d6a23a2f-ca18-4b15-bc73-3e636f99e6bc')(it)('should only include types in the enabledForClasses list', async () => {
|
||||
const schemaController = await parseServer.config.databaseController.loadSchema();
|
||||
await schemaController.addClassIfNotExists('SuperCar', {
|
||||
foo: { type: 'String' },
|
||||
@@ -1426,7 +1426,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(data.userType).toBeNull();
|
||||
expect(data.superCarType).toBeTruthy();
|
||||
});
|
||||
it('should not include types in the disabledForClasses list', async () => {
|
||||
it_id('1db2aceb-d24e-4929-ba43-8dbb5d0395e1')(it)('should not include types in the disabledForClasses list', async () => {
|
||||
const schemaController = await parseServer.config.databaseController.loadSchema();
|
||||
await schemaController.addClassIfNotExists('SuperCar', {
|
||||
foo: { type: 'String' },
|
||||
@@ -1457,7 +1457,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(data.superCarType).toBeNull();
|
||||
expect(data.userType).toBeTruthy();
|
||||
});
|
||||
it('should remove query operations when disabled', async () => {
|
||||
it_id('85c2e02f-0239-4819-b66e-392e0125f6c5')(it)('should remove query operations when disabled', async () => {
|
||||
const superCar = new Parse.Object('SuperCar');
|
||||
await superCar.save({ foo: 'bar' });
|
||||
const customer = new Parse.Object('Customer');
|
||||
@@ -1563,7 +1563,7 @@ describe('ParseGraphQLServer', () => {
|
||||
).toBeRejected();
|
||||
});
|
||||
|
||||
it('should remove mutation operations, create, update and delete, when disabled', async () => {
|
||||
it_id('972161a6-8108-4e99-a1a5-71d0267d26c2')(it)('should remove mutation operations, create, update and delete, when disabled', async () => {
|
||||
const superCar1 = new Parse.Object('SuperCar');
|
||||
await superCar1.save({ foo: 'bar' });
|
||||
const customer1 = new Parse.Object('Customer');
|
||||
@@ -1735,7 +1735,7 @@ describe('ParseGraphQLServer', () => {
|
||||
).toBeRejected();
|
||||
});
|
||||
|
||||
it('should only allow the supplied create and update fields for a class', async () => {
|
||||
it_id('4af763b1-ff86-43c7-ba30-060a1c07e730')(it)('should only allow the supplied create and update fields for a class', async () => {
|
||||
const schemaController = await parseServer.config.databaseController.loadSchema();
|
||||
await schemaController.addClassIfNotExists('SuperCar', {
|
||||
engine: { type: 'String' },
|
||||
@@ -1823,7 +1823,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(updatedSuperCar).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should handle required fields from the Parse class', async () => {
|
||||
it_id('fc9237e9-3e63-4b55-9c1d-e6269f613a93')(it)('should handle required fields from the Parse class', async () => {
|
||||
const schemaController = await parseServer.config.databaseController.loadSchema();
|
||||
await schemaController.addClassIfNotExists('SuperCar', {
|
||||
engine: { type: 'String', required: true },
|
||||
@@ -1875,7 +1875,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(__type2.fields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL');
|
||||
});
|
||||
|
||||
it('should only allow the supplied output fields for a class', async () => {
|
||||
it_id('83b6895a-7dfd-4e3b-a5ce-acdb1fa39705')(it)('should only allow the supplied output fields for a class', async () => {
|
||||
const schemaController = await parseServer.config.databaseController.loadSchema();
|
||||
|
||||
await schemaController.addClassIfNotExists('SuperCar', {
|
||||
@@ -1992,7 +1992,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(getSuperCar.objectId).toBe(superCar.id);
|
||||
});
|
||||
|
||||
it('should only allow the supplied constraint fields for a class', async () => {
|
||||
it_id('67dfcf94-92fb-45a3-a012-3b22c81899ba')(it)('should only allow the supplied constraint fields for a class', async () => {
|
||||
try {
|
||||
const schemaController = await parseServer.config.databaseController.loadSchema();
|
||||
|
||||
@@ -2067,7 +2067,7 @@ describe('ParseGraphQLServer', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should only allow the supplied sort fields for a class', async () => {
|
||||
it_id('a3bdbd5d-8779-42fe-91a1-7a7f90a6177b')(it)('should only allow the supplied sort fields for a class', async () => {
|
||||
const schemaController = await parseServer.config.databaseController.loadSchema();
|
||||
|
||||
await schemaController.addClassIfNotExists('SuperCar', {
|
||||
@@ -4921,7 +4921,7 @@ describe('ParseGraphQLServer', () => {
|
||||
).toEqual(['someValue1', 'someValue2']);
|
||||
});
|
||||
|
||||
it('should support full text search', async () => {
|
||||
it_id('accc59be-fd13-46c5-a103-ec63f2ad6670')(it)('should support full text search', async () => {
|
||||
try {
|
||||
const obj = new Parse.Object('FullTextSearchTest');
|
||||
obj.set('field1', 'Parse GraphQL Server');
|
||||
@@ -5032,7 +5032,7 @@ describe('ParseGraphQLServer', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should support order, skip and first arguments', async () => {
|
||||
it_id('0fd03d3c-a2c8-4fac-95cc-2391a3032ca2')(it)('should support order, skip and first arguments', async () => {
|
||||
const promises = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const obj = new Parse.Object('SomeClass');
|
||||
@@ -5079,7 +5079,7 @@ describe('ParseGraphQLServer', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support pagination', async () => {
|
||||
it_id('588a70c6-2932-4d3b-a838-a74c59d8cffb')(it)('should support pagination', async () => {
|
||||
const numberArray = (first, last) => {
|
||||
const array = [];
|
||||
for (let i = first; i <= last; i++) {
|
||||
@@ -5221,7 +5221,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true);
|
||||
});
|
||||
|
||||
it('should support count', async () => {
|
||||
it_id('4f6a5f20-9642-4cf0-b31d-e739672a9096')(it)('should support count', async () => {
|
||||
await prepareData();
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
@@ -5324,7 +5324,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(result.data.find.count).toEqual(2);
|
||||
});
|
||||
|
||||
it('should respect max limit', async () => {
|
||||
it_id('942b57be-ca8a-4a5b-8104-2adef8743b1a')(it)('should respect max limit', async () => {
|
||||
parseServer = await global.reconfigureServer({
|
||||
maxLimit: 10,
|
||||
});
|
||||
@@ -5365,7 +5365,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(result.data.find.count).toEqual(100);
|
||||
});
|
||||
|
||||
it('should support keys argument', async () => {
|
||||
it_id('952634f0-0ad5-4a08-8da2-187c1bd9ee94')(it)('should support keys argument', async () => {
|
||||
await prepareData();
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
@@ -5771,7 +5771,7 @@ describe('ParseGraphQLServer', () => {
|
||||
).toEqual([object3.id, object1.id, object2.id]);
|
||||
});
|
||||
|
||||
it('should support including relation', async () => {
|
||||
it_id('47a6adf3-1cb4-4d92-b74c-e480363f9cb5')(it)('should support including relation', async () => {
|
||||
await prepareData();
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
@@ -6676,7 +6676,7 @@ describe('ParseGraphQLServer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should unset fields when null used on update/create', async () => {
|
||||
it_id('f722e98e-1fd7-45c5-ade3-5177e3d542e8')(it)('should unset fields when null used on update/create', async () => {
|
||||
const customerSchema = new Parse.Schema('Customer');
|
||||
customerSchema.addString('aString');
|
||||
customerSchema.addBoolean('aBoolean');
|
||||
@@ -8320,7 +8320,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(schema.fields.updatedAt.type).toEqual('Date');
|
||||
});
|
||||
|
||||
it('should support ACL', async () => {
|
||||
it_id('93e748f6-ad9b-4c31-8e1e-c5685e2382fb')(it)('should support ACL', async () => {
|
||||
const someClass = new Parse.Object('SomeClass');
|
||||
await someClass.save();
|
||||
|
||||
@@ -9074,7 +9074,7 @@ describe('ParseGraphQLServer', () => {
|
||||
expect(result2.companies.edges[0].node.objectId).toEqual(company1.id);
|
||||
});
|
||||
|
||||
it('should support relational where query', async () => {
|
||||
it_id('f4312f2c-90bb-4583-b033-02078ae0ce84')(it)('should support relational where query', async () => {
|
||||
const president = new Parse.Object('President');
|
||||
president.set('name', 'James');
|
||||
await president.save();
|
||||
@@ -10374,7 +10374,7 @@ describe('ParseGraphQLServer', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should support Bytes', async () => {
|
||||
it_id('43303db7-c5a7-4bc0-91c3-57e03fffa225')(it)('should support Bytes', async () => {
|
||||
try {
|
||||
const someFieldValue = 'aGVsbG8gd29ybGQ=';
|
||||
|
||||
@@ -10461,7 +10461,7 @@ describe('ParseGraphQLServer', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should support Geo Points', async () => {
|
||||
it_id('6a253e47-6959-4427-b841-c0c1fa77cf01')(it)('should support Geo Points', async () => {
|
||||
try {
|
||||
const someFieldValue = {
|
||||
__typename: 'GeoPoint',
|
||||
|
||||
+12
-12
@@ -54,7 +54,7 @@ describe('Hooks', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should CRUD a function registration', done => {
|
||||
it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)('should CRUD a function registration', done => {
|
||||
// Create
|
||||
Parse.Hooks.createFunction('My-Test-Function', 'http://someurl')
|
||||
.then(response => {
|
||||
@@ -98,7 +98,7 @@ describe('Hooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should CRUD a trigger registration', done => {
|
||||
it_id('7a81069e-2ee9-47fb-8e27-1120eda09e99')(it)('should CRUD a trigger registration', done => {
|
||||
// Create
|
||||
Parse.Hooks.createTrigger('MyClass', 'beforeDelete', 'http://someurl')
|
||||
.then(
|
||||
@@ -188,7 +188,7 @@ describe('Hooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail trying to create two times the same function', done => {
|
||||
it_id('f7ad092f-81dc-4729-afd1-3b02db2f0948')(it)('should fail trying to create two times the same function', done => {
|
||||
Parse.Hooks.createFunction('my_new_function', 'http://url.com')
|
||||
.then(() => jasmine.timeout())
|
||||
.then(
|
||||
@@ -224,7 +224,7 @@ describe('Hooks', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should fail trying to create two times the same trigger', done => {
|
||||
it_id('4db8c249-9174-4e8e-b959-55c8ea959a02')(it)('should fail trying to create two times the same trigger', done => {
|
||||
Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com')
|
||||
.then(
|
||||
() => {
|
||||
@@ -359,7 +359,7 @@ describe('Hooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create hooks and properly preload them', done => {
|
||||
it_id('96d99414-b739-4e36-b3f4-8135e0be83ea')(it)('should create hooks and properly preload them', done => {
|
||||
const promises = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
promises.push(
|
||||
@@ -410,7 +410,7 @@ describe('Hooks', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should run the function on the test server', done => {
|
||||
it_id('fe7d41eb-e570-4804-ac1f-8b6c407fdafe')(it)('should run the function on the test server', done => {
|
||||
app.post('/SomeFunction', function (req, res) {
|
||||
res.json({ success: 'OK!' });
|
||||
});
|
||||
@@ -439,7 +439,7 @@ describe('Hooks', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should run the function on the test server (error handling)', done => {
|
||||
it_id('63985b4c-a212-4a86-aa0e-eb4600bb485b')(it)('should run the function on the test server (error handling)', done => {
|
||||
app.post('/SomeFunctionError', function (req, res) {
|
||||
res.json({ error: { code: 1337, error: 'hacking that one!' } });
|
||||
});
|
||||
@@ -473,7 +473,7 @@ describe('Hooks', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should provide X-Parse-Webhook-Key when defined', done => {
|
||||
it_id('bacc1754-2a3a-4a7a-8d0e-f80af36da1ef')(it)('should provide X-Parse-Webhook-Key when defined', done => {
|
||||
app.post('/ExpectingKey', function (req, res) {
|
||||
if (req.get('X-Parse-Webhook-Key') === 'hook') {
|
||||
res.json({ success: 'correct key provided' });
|
||||
@@ -506,7 +506,7 @@ describe('Hooks', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should not pass X-Parse-Webhook-Key if not provided', done => {
|
||||
it_id('eeb67946-42c6-4581-89af-2abb4927913e')(it)('should not pass X-Parse-Webhook-Key if not provided', done => {
|
||||
reconfigureServer({ webhookKey: undefined }).then(() => {
|
||||
app.post('/ExpectingKeyAlso', function (req, res) {
|
||||
if (req.get('X-Parse-Webhook-Key') === 'hook') {
|
||||
@@ -545,7 +545,7 @@ describe('Hooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should run the beforeSave hook on the test server', done => {
|
||||
it_id('21decb65-4b93-4791-85a3-ab124a9ea3ac')(it)('should run the beforeSave hook on the test server', done => {
|
||||
let triggerCount = 0;
|
||||
app.post('/BeforeSaveSome', function (req, res) {
|
||||
triggerCount++;
|
||||
@@ -576,7 +576,7 @@ describe('Hooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('beforeSave hooks should correctly handle responses containing entire object', done => {
|
||||
it_id('52e3152b-5514-4418-9e76-1f394368b8fb')(it)('beforeSave hooks should correctly handle responses containing entire object', done => {
|
||||
app.post('/BeforeSaveSome2', function (req, res) {
|
||||
const object = Parse.Object.fromJSON(req.body.object);
|
||||
object.set('hello', 'world');
|
||||
@@ -600,7 +600,7 @@ describe('Hooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should run the afterSave hook on the test server', done => {
|
||||
it_id('d27a7587-abb5-40d5-9805-051ee91de474')(it)('should run the afterSave hook on the test server', done => {
|
||||
let triggerCount = 0;
|
||||
let newObjectId;
|
||||
app.post('/AfterSaveSome', function (req, res) {
|
||||
|
||||
@@ -406,7 +406,7 @@ describe('Installations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('updating with new channels', done => {
|
||||
it_id('95955e90-04bc-4437-920e-b84bc30dba01')(it)('updating with new channels', done => {
|
||||
const input = {
|
||||
installationId: '12345678-abcd-abcd-abcd-123456789abc',
|
||||
deviceType: 'android',
|
||||
@@ -856,7 +856,7 @@ describe('Installations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('update is linking two existing objects w/ increment', done => {
|
||||
it_id('22311bc7-3f4f-42c1-a958-57083929e80d')(it)('update is linking two existing objects w/ increment', done => {
|
||||
const installId = '12345678-abcd-abcd-abcd-123456789abc';
|
||||
const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
|
||||
let input = {
|
||||
@@ -969,7 +969,7 @@ describe('Installations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('update is linking two existing with installation id w/ op', done => {
|
||||
it_id('f2975078-eab7-4287-a932-288842e3cfb9')(it)('update is linking two existing with installation id w/ op', done => {
|
||||
const installId = '12345678-abcd-abcd-abcd-123456789abc';
|
||||
const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
|
||||
let input = {
|
||||
@@ -1239,7 +1239,7 @@ describe('Installations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('can use push with beforeSave', async () => {
|
||||
it_id('e581faea-c1b4-4c64-af8c-52287ce6cd06')(it)('can use push with beforeSave', async () => {
|
||||
const input = {
|
||||
deviceToken: '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306',
|
||||
deviceType: 'ios',
|
||||
|
||||
@@ -4,7 +4,7 @@ const UserController = require('../lib/Controllers/UserController').UserControll
|
||||
const Config = require('../lib/Config');
|
||||
const ParseServer = require('../lib/index').ParseServer;
|
||||
const triggers = require('../lib/triggers');
|
||||
const { resolvingPromise, sleep } = require('./support/testUtils');
|
||||
const { resolvingPromise, sleep } = require('../lib/TestUtils');
|
||||
const validatorFail = () => {
|
||||
throw 'you are not authorized';
|
||||
};
|
||||
@@ -878,7 +878,7 @@ describe('ParseLiveQuery', function () {
|
||||
await expectAsync(query.subscribe()).toBeRejectedWith(new Error('Invalid session token'));
|
||||
});
|
||||
|
||||
it('handle invalid websocket payload length', async done => {
|
||||
it_id('4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e')(it)('handle invalid websocket payload length', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
@@ -915,7 +915,7 @@ describe('ParseLiveQuery', function () {
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
it('should execute live query update on email validation', async done => {
|
||||
it_id('39a9191f-26dd-4e05-a379-297a67928de8')(it)('should execute live query update on email validation', async done => {
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: () => {},
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
@@ -1145,7 +1145,7 @@ describe('ParseLiveQuery', function () {
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('does shutdown liveQuery server', async () => {
|
||||
it_id('2f95d8a9-7675-45ba-a4a6-e45cb7efb1fb')(it)('does shutdown liveQuery server', async () => {
|
||||
await reconfigureServer({ appId: 'test_app_id' });
|
||||
const config = {
|
||||
appId: 'hello_test',
|
||||
|
||||
@@ -603,7 +603,7 @@ describe('Parse.Object testing', () => {
|
||||
expect(result.get('items')).toEqual(obj.get('items'));
|
||||
});
|
||||
|
||||
it('can query array nested fields', async () => {
|
||||
it_id('44097c6f-d0ca-4dc5-aa8a-3dd2d9ac645a')(it)('can query array nested fields', async () => {
|
||||
const objects = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const obj = new TestObject();
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('Parse.Polygon testing', () => {
|
||||
}, done.fail);
|
||||
});
|
||||
|
||||
it('polygon equalTo (open/closed) path', done => {
|
||||
it_id('3019353b-d5b3-4e53-bcb1-716418328bdd')(it)('polygon equalTo (open/closed) path', done => {
|
||||
const openPoints = [
|
||||
[0, 0],
|
||||
[0, 1],
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('group by field', done => {
|
||||
it_id('add7050f-65d5-4a13-b526-5bd1ee09c7f1')(it)('group by field', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$group: { _id: '$name' },
|
||||
@@ -115,7 +115,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('group by pipeline operator', async () => {
|
||||
it_id('0ab0d776-e45d-419a-9b35-3d11933b77d1')(it)('group by pipeline operator', async () => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
pipeline: {
|
||||
@@ -133,7 +133,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
expect(resp.results[2].objectId).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('group by empty object', done => {
|
||||
it_id('b6b42145-7eb4-47aa-ada6-8c1444420e07')(it)('group by empty object', done => {
|
||||
const obj = new TestObject();
|
||||
const pipeline = [
|
||||
{
|
||||
@@ -152,7 +152,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('group by empty string', done => {
|
||||
it_id('0f5f6869-e675-41b9-9ad2-52b201124fb0')(it)('group by empty string', done => {
|
||||
const obj = new TestObject();
|
||||
const pipeline = [
|
||||
{
|
||||
@@ -171,7 +171,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('group by empty array', done => {
|
||||
it_id('b9c4f1b4-47f4-4ff4-88fb-586711f57e4a')(it)('group by empty array', done => {
|
||||
const obj = new TestObject();
|
||||
const pipeline = [
|
||||
{
|
||||
@@ -190,7 +190,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('group by multiple columns ', done => {
|
||||
it_id('bf5ee3e5-986c-4994-9c8d-79310283f602')(it)('group by multiple columns ', done => {
|
||||
const obj1 = new TestObject();
|
||||
const obj2 = new TestObject();
|
||||
const obj3 = new TestObject();
|
||||
@@ -216,7 +216,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('group by date object', done => {
|
||||
it_id('3e652c61-78e1-4541-83ac-51ad1def9874')(it)('group by date object', done => {
|
||||
const obj1 = new TestObject();
|
||||
const obj2 = new TestObject();
|
||||
const obj3 = new TestObject();
|
||||
@@ -246,7 +246,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('group by date object transform', done => {
|
||||
it_id('5d3a0f73-1f49-46f3-9be5-caf1eaefec79')(it)('group by date object transform', done => {
|
||||
const obj1 = new TestObject();
|
||||
const obj2 = new TestObject();
|
||||
const obj3 = new TestObject();
|
||||
@@ -276,7 +276,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('group by number', done => {
|
||||
it_id('1f9b10f7-dc0e-467f-b506-a303b9c36258')(it)('group by number', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$group: { _id: '$score' },
|
||||
@@ -296,7 +296,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('group and multiply transform', done => {
|
||||
it_id('c7695018-03de-49e4-8a72-d4d956f70deb')(it_exclude_dbs(['postgres']))('group and multiply transform', done => {
|
||||
const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 });
|
||||
const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 });
|
||||
const pipeline = [
|
||||
@@ -319,7 +319,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('project and multiply transform', done => {
|
||||
it_id('2d278175-7594-4b29-bef4-04c778b7a42f')(it_exclude_dbs(['postgres']))('project and multiply transform', done => {
|
||||
const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 });
|
||||
const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 });
|
||||
const pipeline = [
|
||||
@@ -351,7 +351,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('project without objectId transform', done => {
|
||||
it_id('9c9d9318-3a9e-4c2a-8a09-d3aa52c7505b')(it_exclude_dbs(['postgres']))('project without objectId transform', done => {
|
||||
const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 });
|
||||
const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 });
|
||||
const pipeline = [
|
||||
@@ -383,7 +383,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('project updatedAt only transform', done => {
|
||||
it_id('f92c82ac-1993-4758-b718-45689dfc4154')(it_exclude_dbs(['postgres']))('project updatedAt only transform', done => {
|
||||
const pipeline = [
|
||||
{
|
||||
$project: { _id: 0, updatedAt: 1 },
|
||||
@@ -401,9 +401,9 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])(
|
||||
'can group by any date field (it does not work if you have dirty data)', // rows in your collection with non date data in the field that is supposed to be a date
|
||||
it_id('99566b1d-778d-4444-9deb-c398108e659d')(it_exclude_dbs(['postgres']))('can group by any date field (it does not work if you have dirty data)',
|
||||
done => {
|
||||
// rows in your collection with non date data in the field that is supposed to be a date
|
||||
const obj1 = new TestObject({ dateField2019: new Date(1990, 11, 1) });
|
||||
const obj2 = new TestObject({ dateField2019: new Date(1990, 5, 1) });
|
||||
const obj3 = new TestObject({ dateField2019: new Date(1990, 11, 1) });
|
||||
@@ -472,7 +472,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
}
|
||||
);
|
||||
|
||||
it('group by pointer', done => {
|
||||
it_id('bf3c2704-b721-4b1b-92fa-e1b129ae4aff')(it)('group by pointer', done => {
|
||||
const pointer1 = new TestObject();
|
||||
const pointer2 = new TestObject();
|
||||
const obj1 = new TestObject({ pointer: pointer1 });
|
||||
@@ -493,7 +493,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('group sum query', done => {
|
||||
it_id('9ee9e8c0-a590-4af9-97a9-4b8e5080ffae')(it)('group sum query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$group: { _id: null, total: { $sum: '$score' } },
|
||||
@@ -509,7 +509,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('group count query', done => {
|
||||
it_id('39133cd6-5bdf-4917-b672-a9d7a9157b6f')(it)('group count query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$group: { _id: null, total: { $sum: 1 } },
|
||||
@@ -525,7 +525,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('group min query', done => {
|
||||
it_id('48685ff3-066f-4353-82e7-87f39d812ff7')(it)('group min query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$group: { _id: null, minScore: { $min: '$score' } },
|
||||
@@ -541,7 +541,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('group max query', done => {
|
||||
it_id('581efea6-6525-4e10-96d9-76d32c73e7a9')(it)('group max query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$group: { _id: null, maxScore: { $max: '$score' } },
|
||||
@@ -557,7 +557,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('group avg query', done => {
|
||||
it_id('5f880de2-b97f-43d1-89b7-ad903a4be4e2')(it)('group avg query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$group: { _id: null, avgScore: { $avg: '$score' } },
|
||||
@@ -573,7 +573,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('limit query', done => {
|
||||
it_id('58e7a1a0-fae1-4993-b336-7bcbd5b7c786')(it)('limit query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$limit: 2,
|
||||
@@ -587,7 +587,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('sort ascending query', done => {
|
||||
it_id('c892a3d2-8ae8-4b88-bf2b-3c958e1cacd8')(it)('sort ascending query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$sort: { name: 1 },
|
||||
@@ -605,7 +605,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('sort decending query', done => {
|
||||
it_id('79d4bc2e-8b69-42ec-8526-20d17e968ab3')(it)('sort decending query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$sort: { name: -1 },
|
||||
@@ -623,7 +623,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('skip query', done => {
|
||||
it_id('b3d97d48-bd6b-444d-be64-cc1fd4738266')(it)('skip query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$skip: 2,
|
||||
@@ -637,7 +637,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('match comparison date query', done => {
|
||||
it_id('4a7daee3-5ba1-4c8b-b406-1846a73a64c8')(it)('match comparison date query', done => {
|
||||
const today = new Date();
|
||||
const yesterday = new Date();
|
||||
const tomorrow = new Date();
|
||||
@@ -658,7 +658,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should aggregate with Date object (directAccess)', async () => {
|
||||
it_id('d98c8c20-6dac-4d74-8228-85a1ae46a7d0')(it)('should aggregate with Date object (directAccess)', async () => {
|
||||
const rest = require('../lib/rest');
|
||||
const auth = require('../lib/Auth');
|
||||
const TestObject = Parse.Object.extend('TestObject');
|
||||
@@ -675,7 +675,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
expect(resp.results.length).toBe(1);
|
||||
});
|
||||
|
||||
it('match comparison query', done => {
|
||||
it_id('3d73d23a-fce1-4ac0-972a-50f6a550f348')(it)('match comparison query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$match: { score: { $gt: 15 } },
|
||||
@@ -690,7 +690,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('match multiple comparison query', done => {
|
||||
it_id('11772059-6c93-41ac-8dfe-e55b6c97e16f')(it)('match multiple comparison query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$match: { score: { $gt: 5, $lt: 15 } },
|
||||
@@ -707,7 +707,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('match complex comparison query', done => {
|
||||
it_id('ca2efb04-8f73-40ca-a5fc-79d0032bc398')(it)('match complex comparison query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$match: { score: { $gt: 5, $lt: 15 }, views: { $gt: 850, $lt: 1000 } },
|
||||
@@ -723,7 +723,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('match comparison and equality query', done => {
|
||||
it_id('5ef9dcbe-fe54-4db2-b8fb-58c87c6ff072')(it)('match comparison and equality query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$match: { score: { $gt: 5, $lt: 15 }, views: 900 },
|
||||
@@ -739,7 +739,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('match $or query', done => {
|
||||
it_id('c910a6af-58df-46aa-bbf8-da014a04cdcd')(it)('match $or query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$match: {
|
||||
@@ -762,7 +762,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('match objectId query', done => {
|
||||
it_id('0f768dc2-0675-4e45-a763-5ca9c895fa5f')(it)('match objectId query', done => {
|
||||
const obj1 = new TestObject();
|
||||
const obj2 = new TestObject();
|
||||
Parse.Object.saveAll([obj1, obj2])
|
||||
@@ -778,7 +778,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('match field query', done => {
|
||||
it_id('27349e04-0d9d-453f-ad85-1a811631582d')(it)('match field query', done => {
|
||||
const obj1 = new TestObject({ name: 'TestObject1' });
|
||||
const obj2 = new TestObject({ name: 'TestObject2' });
|
||||
Parse.Object.saveAll([obj1, obj2])
|
||||
@@ -794,7 +794,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('match pointer query', done => {
|
||||
it_id('9222e025-d450-4699-8d5b-c5cf9a64fb24')(it)('match pointer query', done => {
|
||||
const pointer1 = new PointerObject();
|
||||
const pointer2 = new PointerObject();
|
||||
const obj1 = new TestObject({ pointer: pointer1 });
|
||||
@@ -817,7 +817,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('match exists query', done => {
|
||||
it_id('3a1e2cdc-52c7-4060-bc90-b06d557d85ce')(it_exclude_dbs(['postgres']))('match exists query', done => {
|
||||
const pipeline = [{ $match: { score: { $exists: true } } }];
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.aggregate(pipeline).then(results => {
|
||||
@@ -826,7 +826,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('match date query - createdAt', done => {
|
||||
it_id('0adea3f4-73f7-4b48-a7dd-c764ceb947ec')(it)('match date query - createdAt', done => {
|
||||
const obj1 = new TestObject();
|
||||
const obj2 = new TestObject();
|
||||
|
||||
@@ -845,7 +845,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('match date query - updatedAt', done => {
|
||||
it_id('cdc0eecb-f547-4881-84cc-c06fb46a636a')(it)('match date query - updatedAt', done => {
|
||||
const obj1 = new TestObject();
|
||||
const obj2 = new TestObject();
|
||||
|
||||
@@ -864,7 +864,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('match date query - empty', done => {
|
||||
it_id('621fe00a-1127-4341-a8e1-fc579b7ed8bd')(it)('match date query - empty', done => {
|
||||
const obj1 = new TestObject();
|
||||
const obj2 = new TestObject();
|
||||
|
||||
@@ -882,7 +882,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('match pointer with operator query', done => {
|
||||
it_id('802ffc99-861b-4b72-90a6-0c666a2e3fd8')(it_exclude_dbs(['postgres']))('match pointer with operator query', done => {
|
||||
const pointer = new PointerObject();
|
||||
|
||||
const obj1 = new TestObject({ pointer });
|
||||
@@ -905,7 +905,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('match null values', async () => {
|
||||
it_id('28090280-7c3e-47f8-8bf6-bebf8566a36c')(it_exclude_dbs(['postgres']))('match null values', async () => {
|
||||
const obj1 = new Parse.Object('MyCollection');
|
||||
obj1.set('language', 'en');
|
||||
obj1.set('otherField', 1);
|
||||
@@ -955,7 +955,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
).toEqual([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('project query', done => {
|
||||
it_id('df63d1f5-7c37-4ed9-8bc5-20d82f29f509')(it)('project query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$project: { name: 1 },
|
||||
@@ -975,7 +975,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('multiple project query', done => {
|
||||
it_id('69224bbb-8ea0-4ab4-af23-398b6432f668')(it)('multiple project query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$project: { name: 1, score: 1, sender: 1 },
|
||||
@@ -995,7 +995,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('project pointer query', done => {
|
||||
it_id('97ce4c7c-8d9f-4ffd-9352-394bc9867bab')(it)('project pointer query', done => {
|
||||
const pointer = new PointerObject();
|
||||
const obj = new TestObject({ pointer, name: 'hello' });
|
||||
|
||||
@@ -1018,7 +1018,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('project with group query', done => {
|
||||
it_id('3940aac3-ac49-4279-8083-af9096de636f')(it)('project with group query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$project: { score: 1 },
|
||||
@@ -1074,7 +1074,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('distinct query', done => {
|
||||
it_id('985e7a66-d4f5-4f72-bd54-ee44670e0ab0')(it)('distinct query', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: { distinct: 'score' },
|
||||
});
|
||||
@@ -1088,7 +1088,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('distinct query with where', done => {
|
||||
it_id('ef157f86-c456-4a4c-8dac-81910bd0f716')(it)('distinct query with where', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
distinct: 'score',
|
||||
@@ -1105,7 +1105,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('distinct query with where string', done => {
|
||||
it_id('7f5275cc-2c34-42bc-8a09-43378419c326')(it)('distinct query with where string', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
distinct: 'score',
|
||||
@@ -1120,7 +1120,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('distinct nested', done => {
|
||||
it_id('383b7248-e457-4373-8d5c-f9359384347e')(it)('distinct nested', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: { distinct: 'sender.group' },
|
||||
});
|
||||
@@ -1134,7 +1134,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('distinct pointer', done => {
|
||||
it_id('20f14464-adb7-428c-ac7a-5a91a1952a64')(it)('distinct pointer', done => {
|
||||
const pointer1 = new PointerObject();
|
||||
const pointer2 = new PointerObject();
|
||||
const obj1 = new TestObject({ pointer: pointer1 });
|
||||
@@ -1153,7 +1153,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('distinct class does not exist return empty', done => {
|
||||
it_id('91e6cb94-2837-44b7-b057-0c4965057caa')(it)('distinct class does not exist return empty', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: { distinct: 'unknown' },
|
||||
});
|
||||
@@ -1165,7 +1165,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('distinct field does not exist return empty', done => {
|
||||
it_id('bd15daaf-8dc7-458c-81e2-170026f4a8a7')(it)('distinct field does not exist return empty', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: { distinct: 'unknown' },
|
||||
});
|
||||
@@ -1182,7 +1182,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('distinct array', done => {
|
||||
it_id('21988fce-8326-425f-82f0-cd444ca3671b')(it)('distinct array', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: { distinct: 'size' },
|
||||
});
|
||||
@@ -1197,13 +1197,13 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('distinct objectId', async () => {
|
||||
it_id('633fde06-c4af-474b-9841-3ccabc24dd4f')(it)('distinct objectId', async () => {
|
||||
const query = new Parse.Query(TestObject);
|
||||
const results = await query.distinct('objectId');
|
||||
expect(results.length).toBe(4);
|
||||
});
|
||||
|
||||
it('distinct createdAt', async () => {
|
||||
it_id('8f9706f4-2703-42f1-b524-f2f7e72bbfe7')(it)('distinct createdAt', async () => {
|
||||
const object1 = new TestObject({ createdAt_test: true });
|
||||
await object1.save();
|
||||
const object2 = new TestObject({ createdAt_test: true });
|
||||
@@ -1214,7 +1214,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
expect(results.length).toBe(2);
|
||||
});
|
||||
|
||||
it('distinct updatedAt', async () => {
|
||||
it_id('3562e600-8ce5-4d6d-96df-8ff969e81421')(it)('distinct updatedAt', async () => {
|
||||
const object1 = new TestObject({ updatedAt_test: true });
|
||||
await object1.save();
|
||||
const object2 = new TestObject();
|
||||
@@ -1227,7 +1227,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
expect(results.length).toBe(2);
|
||||
});
|
||||
|
||||
it('distinct null field', done => {
|
||||
it_id('5012cfb1-b0aa-429d-a94f-d32d8aa0b7f9')(it)('distinct null field', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: { distinct: 'distinctField' },
|
||||
});
|
||||
@@ -1256,7 +1256,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('does not return sensitive hidden properties', done => {
|
||||
it_id('d9c19419-e99d-4d9f-b7f3-418e49ee47dd')(it)('does not return sensitive hidden properties', done => {
|
||||
const options = Object.assign({}, masterKeyOptions, {
|
||||
body: {
|
||||
$match: {
|
||||
@@ -1305,7 +1305,7 @@ describe('Parse.Query Aggregate testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('aggregate allow multiple of same stage', async done => {
|
||||
it_id('0a23e791-e9b5-457a-9bf9-9c5ecf406f42')(it_exclude_dbs(['postgres']))('aggregate allow multiple of same stage', async done => {
|
||||
await reconfigureServer({ silent: false });
|
||||
const pointer1 = new TestObject({ value: 1 });
|
||||
const pointer2 = new TestObject({ value: 2 });
|
||||
|
||||
@@ -30,7 +30,7 @@ const fullTextHelper = async () => {
|
||||
};
|
||||
|
||||
describe('Parse.Query Full Text Search testing', () => {
|
||||
it('fullTextSearch: $search', async () => {
|
||||
it_id('77ba6779-6584-4e09-8e7e-31f89e741d6a')(it)('fullTextSearch: $search', async () => {
|
||||
await fullTextHelper();
|
||||
const query = new Parse.Query('TestObject');
|
||||
query.fullText('subject', 'coffee');
|
||||
@@ -38,7 +38,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
||||
expect(results.length).toBe(3);
|
||||
});
|
||||
|
||||
it('fullTextSearch: $search, sort', async () => {
|
||||
it_id('d1992ea6-6d92-4bfa-a487-2a49fbcf8f0d')(it)('fullTextSearch: $search, sort', async () => {
|
||||
await fullTextHelper();
|
||||
const query = new Parse.Query('TestObject');
|
||||
query.fullText('subject', 'coffee');
|
||||
@@ -51,7 +51,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
||||
expect(results[2].get('score'));
|
||||
});
|
||||
|
||||
it('fulltext descending by $score', async () => {
|
||||
it_id('07172595-50de-4be2-984a-d3136bebb22e')(it)('fulltext descending by $score', async () => {
|
||||
await fullTextHelper();
|
||||
const query = new Parse.Query('TestObject');
|
||||
query.fullText('subject', 'coffee');
|
||||
@@ -68,7 +68,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
||||
expect(second.get('score') >= third.get('score')).toBeTrue();
|
||||
});
|
||||
|
||||
it('fullTextSearch: $language', async () => {
|
||||
it_id('8e821973-3fae-4e7c-8152-766228a18cdd')(it)('fullTextSearch: $language', async () => {
|
||||
await fullTextHelper();
|
||||
const query = new Parse.Query('TestObject');
|
||||
query.fullText('subject', 'leche', { language: 'spanish' });
|
||||
@@ -76,7 +76,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
||||
expect(resp.length).toBe(2);
|
||||
});
|
||||
|
||||
it('fullTextSearch: $diacriticSensitive', async () => {
|
||||
it_id('7d3da216-9582-40ee-a2fe-8316feaf5c0c')(it)('fullTextSearch: $diacriticSensitive', async () => {
|
||||
await fullTextHelper();
|
||||
const query = new Parse.Query('TestObject');
|
||||
query.fullText('subject', 'CAFÉ', { diacriticSensitive: true });
|
||||
@@ -84,7 +84,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
||||
expect(resp.length).toBe(1);
|
||||
});
|
||||
|
||||
it('fullTextSearch: $search, invalid input', async () => {
|
||||
it_id('dade10c8-2b9c-4f43-bb3f-a13bbd82ac22')(it)('fullTextSearch: $search, invalid input', async () => {
|
||||
await fullTextHelper();
|
||||
const invalidQuery = async () => {
|
||||
const where = {
|
||||
@@ -114,7 +114,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('fullTextSearch: $language, invalid input', async () => {
|
||||
it_id('ff7c6b1c-4712-4847-bb76-f4e1f641f7b5')(it)('fullTextSearch: $language, invalid input', async () => {
|
||||
await fullTextHelper();
|
||||
const query = new Parse.Query('TestObject');
|
||||
query.fullText('subject', 'leche', { language: true });
|
||||
@@ -123,7 +123,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('fullTextSearch: $caseSensitive, invalid input', async () => {
|
||||
it_id('de262dbc-ec75-4ec6-9217-fbb90146c272')(it)('fullTextSearch: $caseSensitive, invalid input', async () => {
|
||||
await fullTextHelper();
|
||||
const query = new Parse.Query('TestObject');
|
||||
query.fullText('subject', 'leche', { caseSensitive: 'string' });
|
||||
@@ -132,7 +132,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('fullTextSearch: $diacriticSensitive, invalid input', async () => {
|
||||
it_id('b7b7b3a9-8d6c-4f98-a0ff-0113593d06d4')(it)('fullTextSearch: $diacriticSensitive, invalid input', async () => {
|
||||
await fullTextHelper();
|
||||
const query = new Parse.Query('TestObject');
|
||||
query.fullText('subject', 'leche', { diacriticSensitive: 'string' });
|
||||
|
||||
+11
-11
@@ -590,7 +590,7 @@ describe('Parse.Query testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('containsAll object array queries', function (done) {
|
||||
it_id('25bb35a6-e953-4d6d-a31c-66324d5ae076')(it)('containsAll object array queries', function (done) {
|
||||
const MessageSet = Parse.Object.extend({ className: 'MessageSet' });
|
||||
|
||||
const messageList = [];
|
||||
@@ -705,7 +705,7 @@ describe('Parse.Query testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('containsAllStartingWith values must be all of type starting with regex', done => {
|
||||
it_id('3ea6ae04-bcc2-453d-8817-4c64d059c2f6')(it)('containsAllStartingWith values must be all of type starting with regex', done => {
|
||||
const object = new Parse.Object('Object');
|
||||
object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']);
|
||||
|
||||
@@ -884,7 +884,7 @@ describe('Parse.Query testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('containedBy pointer array', done => {
|
||||
it_id('01a15195-dde2-4368-b996-d746a4ede3a1')(it)('containedBy pointer array', done => {
|
||||
const objects = Array.from(Array(10).keys()).map(idx => {
|
||||
const obj = new Parse.Object('Object');
|
||||
obj.set('key', idx);
|
||||
@@ -1668,7 +1668,7 @@ describe('Parse.Query testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('can order on an object number field', function (done) {
|
||||
it_id('65c8238d-cf02-49d0-a919-8a17f5a58280')(it)('can order on an object number field', function (done) {
|
||||
const testSet = [
|
||||
{ sortField: { value: 10 } },
|
||||
{ sortField: { value: 1 } },
|
||||
@@ -1689,7 +1689,7 @@ describe('Parse.Query testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('can order on an object number field (level 2)', function (done) {
|
||||
it_id('d8f0bead-b931-4d66-8b0c-28c5705e463c')(it)('can order on an object number field (level 2)', function (done) {
|
||||
const testSet = [
|
||||
{ sortField: { value: { field: 10 } } },
|
||||
{ sortField: { value: { field: 1 } } },
|
||||
@@ -2111,7 +2111,7 @@ describe('Parse.Query testing', () => {
|
||||
.then(done);
|
||||
});
|
||||
|
||||
it('Use a regex that requires all modifiers', function (done) {
|
||||
it_id('823852f6-1de5-45ba-a2b9-ed952fcc6012')(it)('Use a regex that requires all modifiers', function (done) {
|
||||
const thing = new TestObject();
|
||||
thing.set('myString', 'PArSe\nCom');
|
||||
Parse.Object.saveAll([thing]).then(function () {
|
||||
@@ -3786,7 +3786,7 @@ describe('Parse.Query testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('notEqual with array of pointers', done => {
|
||||
it_id('56b09b92-c756-4bae-8c32-1c32b5b4c397')(it)('notEqual with array of pointers', done => {
|
||||
const children = [];
|
||||
const parents = [];
|
||||
const promises = [];
|
||||
@@ -4003,7 +4003,7 @@ describe('Parse.Query testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should properly interpret a query v2', done => {
|
||||
it_id('7079f0ef-47b3-4a1e-aac0-32654dadaa27')(it)('should properly interpret a query v2', done => {
|
||||
const user = new Parse.User();
|
||||
user.set('username', 'foo');
|
||||
user.set('password', 'bar');
|
||||
@@ -4082,7 +4082,7 @@ describe('Parse.Query testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should find objects with array of pointers', done => {
|
||||
it_id('d95818c0-9e3c-41e6-be20-e7bafb59eefb')(it)('should find objects with array of pointers', done => {
|
||||
const objects = [];
|
||||
while (objects.length != 5) {
|
||||
const object = new Parse.Object('ContainedObject');
|
||||
@@ -5013,7 +5013,7 @@ describe('Parse.Query testing', () => {
|
||||
equal(results[0].get('name'), group2.get('name'));
|
||||
});
|
||||
|
||||
it('withJSON supports geoWithin.centerSphere', done => {
|
||||
it_id('8886b994-fbb8-487d-a863-43bbd2b24b73')(it)('withJSON supports geoWithin.centerSphere', done => {
|
||||
const inbound = new Parse.GeoPoint(1.5, 1.5);
|
||||
const onbound = new Parse.GeoPoint(10, 10);
|
||||
const outbound = new Parse.GeoPoint(20, 20);
|
||||
@@ -5114,7 +5114,7 @@ describe('Parse.Query testing', () => {
|
||||
.catch(() => done());
|
||||
});
|
||||
|
||||
it('can add new config to existing config', async () => {
|
||||
it_id('02d4e7e6-859a-4ab6-878d-135ccc77040e')(it)('can add new config to existing config', async () => {
|
||||
await request({
|
||||
method: 'PUT',
|
||||
url: 'http://localhost:8378/1/config',
|
||||
|
||||
@@ -126,7 +126,7 @@ describe('Parse Role testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should not recursively load the same role multiple times', done => {
|
||||
it_id('b03abe32-e8e4-4666-9b81-9c804aa53400')(it)('should not recursively load the same role multiple times', done => {
|
||||
const rootRole = 'RootRole';
|
||||
const roleNames = ['FooRole', 'BarRole', 'BazRole'];
|
||||
const allRoles = [rootRole].concat(roleNames);
|
||||
|
||||
@@ -2,16 +2,17 @@ const ParseServerRESTController = require('../lib/ParseServerRESTController')
|
||||
.ParseServerRESTController;
|
||||
const ParseServer = require('../lib/ParseServer').default;
|
||||
const Parse = require('parse/node').Parse;
|
||||
const TestUtils = require('../lib/TestUtils');
|
||||
|
||||
let RESTController;
|
||||
|
||||
describe('ParseServerRESTController', () => {
|
||||
let createSpy;
|
||||
beforeEach(() => {
|
||||
RESTController = ParseServerRESTController(
|
||||
Parse.applicationId,
|
||||
ParseServer.promiseRouter({ appId: Parse.applicationId })
|
||||
);
|
||||
createSpy = spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
});
|
||||
|
||||
it('should handle a get request', async () => {
|
||||
@@ -133,24 +134,11 @@ describe('ParseServerRESTController', () => {
|
||||
process.env.PARSE_SERVER_TEST_DB === 'postgres'
|
||||
) {
|
||||
describe('transactions', () => {
|
||||
beforeEach(async () => {
|
||||
await TestUtils.destroyAllDataPermanently(true);
|
||||
if (process.env.MONGODB_TOPOLOGY === 'replicaset') {
|
||||
await reconfigureServer({
|
||||
databaseAdapter: undefined,
|
||||
databaseURI:
|
||||
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
|
||||
});
|
||||
} else {
|
||||
await reconfigureServer();
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle a batch request with transaction = true', async () => {
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject.save();
|
||||
await myObject.destroy();
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
createSpy.calls.reset();
|
||||
const response = await RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
@@ -173,10 +161,10 @@ describe('ParseServerRESTController', () => {
|
||||
expect(response[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
const results = await query.find();
|
||||
expect(databaseAdapter.createObject.calls.count() % 2).toBe(0);
|
||||
for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) {
|
||||
expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe(
|
||||
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
|
||||
expect(createSpy.calls.count()).toBe(2);
|
||||
for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) {
|
||||
expect(createSpy.calls.argsFor(i)[3]).toBe(
|
||||
createSpy.calls.argsFor(i + 1)[3]
|
||||
);
|
||||
}
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
@@ -184,9 +172,11 @@ describe('ParseServerRESTController', () => {
|
||||
|
||||
it('should not save anything when one operation fails in a transaction', async () => {
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject.save();
|
||||
await myObject.save({ key: 'stringField' });
|
||||
await myObject.destroy();
|
||||
createSpy.calls.reset();
|
||||
try {
|
||||
// Saving a number to a string field should fail
|
||||
await RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
@@ -294,20 +284,21 @@ describe('ParseServerRESTController', () => {
|
||||
it('should generate separate session for each call', async () => {
|
||||
await reconfigureServer();
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject.save();
|
||||
await myObject.save({ key: 'stringField' });
|
||||
await myObject.destroy();
|
||||
|
||||
const myObject2 = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject2.save();
|
||||
await myObject2.save({ key: 'stringField' });
|
||||
await myObject2.destroy();
|
||||
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
createSpy.calls.reset();
|
||||
|
||||
let myObjectCalls = 0;
|
||||
Parse.Cloud.beforeSave('MyObject', async () => {
|
||||
myObjectCalls++;
|
||||
if (myObjectCalls === 2) {
|
||||
try {
|
||||
// Saving a number to a string field should fail
|
||||
await RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
@@ -459,14 +450,14 @@ describe('ParseServerRESTController', () => {
|
||||
const results3 = await query3.find();
|
||||
expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
|
||||
expect(databaseAdapter.createObject.calls.count() >= 13).toEqual(true);
|
||||
expect(createSpy.calls.count() >= 13).toEqual(true);
|
||||
let transactionalSession;
|
||||
let transactionalSession2;
|
||||
let myObjectDBCalls = 0;
|
||||
let myObject2DBCalls = 0;
|
||||
let myObject3DBCalls = 0;
|
||||
for (let i = 0; i < databaseAdapter.createObject.calls.count(); i++) {
|
||||
const args = databaseAdapter.createObject.calls.argsFor(i);
|
||||
for (let i = 0; i < createSpy.calls.count(); i++) {
|
||||
const args = createSpy.calls.argsFor(i);
|
||||
switch (args[0]) {
|
||||
case 'MyObject':
|
||||
myObjectDBCalls++;
|
||||
|
||||
@@ -2257,7 +2257,7 @@ describe('Parse.User testing', () => {
|
||||
});
|
||||
|
||||
describe('case insensitive signup not allowed', () => {
|
||||
it('signup should fail with duplicate case insensitive username with basic setter', async () => {
|
||||
it_id('464eddc2-7a46-413d-888e-b43b040f1511')(it)('signup should fail with duplicate case insensitive username with basic setter', async () => {
|
||||
const user = new Parse.User();
|
||||
user.set('username', 'test1');
|
||||
user.set('password', 'test');
|
||||
@@ -2271,7 +2271,7 @@ describe('Parse.User testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('signup should fail with duplicate case insensitive username with field specific setter', async () => {
|
||||
it_id('1cef005b-d5f0-4699-af0c-bb0af27d2437')(it)('signup should fail with duplicate case insensitive username with field specific setter', async () => {
|
||||
const user = new Parse.User();
|
||||
user.setUsername('test1');
|
||||
user.setPassword('test');
|
||||
@@ -2285,7 +2285,7 @@ describe('Parse.User testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('signup should fail with duplicate case insensitive email', async () => {
|
||||
it_id('12735529-98d1-42c0-b437-3b47fe78ddde')(it)('signup should fail with duplicate case insensitive email', async () => {
|
||||
const user = new Parse.User();
|
||||
user.setUsername('test1');
|
||||
user.setPassword('test');
|
||||
@@ -2301,7 +2301,7 @@ describe('Parse.User testing', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('edit should fail with duplicate case insensitive email', async () => {
|
||||
it_id('66e51d52-2420-4b62-8a0d-c7e1b384763e')(it)('edit should fail with duplicate case insensitive email', async () => {
|
||||
const user = new Parse.User();
|
||||
user.setUsername('test1');
|
||||
user.setPassword('test');
|
||||
@@ -2952,7 +2952,7 @@ describe('Parse.User testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should send email when upgrading from anon', async done => {
|
||||
it_id('1be98368-19ac-4c77-8531-762a114f43fb')(it)('should send email when upgrading from anon', async done => {
|
||||
await reconfigureServer();
|
||||
let emailCalled = false;
|
||||
let emailOptions;
|
||||
@@ -3018,7 +3018,7 @@ describe('Parse.User testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not send email when email is not a string', async done => {
|
||||
it_id('bf668670-39fa-44d3-a9a9-cad52f36d272')(it)('should not send email when email is not a string', async done => {
|
||||
let emailCalled = false;
|
||||
let emailOptions;
|
||||
const emailAdapter = {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
const request = require('../lib/request');
|
||||
|
||||
describe('Password Policy: ', () => {
|
||||
it('should show the invalid link page if the user clicks on the password reset link after the token expires', done => {
|
||||
it_id('b400a867-9f05-496f-af79-933aa588dde5')(it)('should show the invalid link page if the user clicks on the password reset link after the token expires', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
@@ -150,7 +150,7 @@ describe('Password Policy: ', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should keep reset token with resetTokenReuseIfValid', async done => {
|
||||
it_id('7d98e1f2-ae89-4038-9ea7-5254854ea42e')(it)('should keep reset token with resetTokenReuseIfValid', async done => {
|
||||
const sendEmailOptions = [];
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
@@ -1164,7 +1164,7 @@ describe('Password Policy: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should succeed if logged in before password expires', done => {
|
||||
it_id('d7d0a93e-efe6-48c0-b622-0f7fb570ccc1')(it)('should succeed if logged in before password expires', done => {
|
||||
const user = new Parse.User();
|
||||
reconfigureServer({
|
||||
appName: 'passwordPolicy',
|
||||
@@ -1197,7 +1197,7 @@ describe('Password Policy: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if logged in after password expires', done => {
|
||||
it_id('22428408-8763-445d-9833-2b2d92008f62')(it)('should fail if logged in after password expires', done => {
|
||||
const user = new Parse.User();
|
||||
reconfigureServer({
|
||||
appName: 'passwordPolicy',
|
||||
@@ -1236,7 +1236,7 @@ describe('Password Policy: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply password expiry policy to existing user upon first login after policy is enabled', done => {
|
||||
it_id('cc97a109-e35f-4f94-b942-3a6134921cdd')(it)('should apply password expiry policy to existing user upon first login after policy is enabled', done => {
|
||||
const user = new Parse.User();
|
||||
reconfigureServer({
|
||||
appName: 'passwordPolicy',
|
||||
@@ -1304,7 +1304,7 @@ describe('Password Policy: ', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset password timestamp when password is reset', done => {
|
||||
it_id('d1e6ab9d-c091-4fea-b952-08b7484bfc89')(it)('should reset password timestamp when password is reset', done => {
|
||||
const user = new Parse.User();
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
|
||||
@@ -226,7 +226,7 @@ describe('Pointer Permissions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should query on pointer permission enabled column', done => {
|
||||
it_id('f38c35e7-d804-4d32-986d-2579e25d2461')(it)('should query on pointer permission enabled column', done => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const user = new Parse.User();
|
||||
const user2 = new Parse.User();
|
||||
@@ -1070,7 +1070,7 @@ describe('Pointer Permissions', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should work with write', async done => {
|
||||
it_id('1bbb9ed6-5558-4ce5-a238-b1a2015d273f')(it)('should work with write', async done => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const user = new Parse.User();
|
||||
const user2 = new Parse.User();
|
||||
@@ -1203,7 +1203,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should query on pointer permission enabled column', async done => {
|
||||
it_id('8a7d188c-b75c-4eac-90b6-9b0b11f873ae')(it)('should query on pointer permission enabled column', async done => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const user = new Parse.User();
|
||||
const user2 = new Parse.User();
|
||||
@@ -2058,7 +2058,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fail for user not listed', async done => {
|
||||
it_id('9ba681d5-59f5-4996-b36d-6647d23e6a44')(it)('should fail for user not listed', async done => {
|
||||
await updateCLP({
|
||||
get: {
|
||||
pointerFields: ['owner'],
|
||||
@@ -2218,7 +2218,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fail for user not listed', async done => {
|
||||
it_id('bcdb158d-c0b6-45e3-84ab-a3636f7cb470')(it)('should fail for user not listed', async done => {
|
||||
await updateCLP({
|
||||
update: {
|
||||
pointerFields: ['owner'],
|
||||
@@ -2270,7 +2270,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fail for user not listed', async done => {
|
||||
it_id('70aa3853-6e26-4c38-a927-2ddb24ced7d4')(it)('should fail for user not listed', async done => {
|
||||
await updateCLP({
|
||||
delete: {
|
||||
pointerFields: ['owner'],
|
||||
@@ -2517,7 +2517,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fail for user not listed', async done => {
|
||||
it_id('84a42339-c7b5-4735-a431-57b46535b073')(it)('should fail for user not listed', async done => {
|
||||
await updateCLP({
|
||||
get: {
|
||||
pointerFields: ['moderators'],
|
||||
@@ -2685,7 +2685,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should be allowed (multiple users in array)', async done => {
|
||||
it_id('2b19234a-a471-48b4-bd1a-27bd286d066f')(it)('should be allowed (multiple users in array)', async done => {
|
||||
await updateCLP({
|
||||
update: {
|
||||
pointerFields: ['moderators'],
|
||||
@@ -2698,7 +2698,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fail for user not listed', async done => {
|
||||
it_id('1abb9f4a-fb24-48c7-8025-3001d6cf8737')(it)('should fail for user not listed', async done => {
|
||||
await updateCLP({
|
||||
update: {
|
||||
pointerFields: ['moderators'],
|
||||
@@ -2764,7 +2764,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fail for user not listed', async done => {
|
||||
it_id('3175a0e3-e51e-4b84-a2e6-50bbcc582123')(it)('should fail for user not listed', async done => {
|
||||
await updateCLP({
|
||||
delete: {
|
||||
pointerFields: ['owners'],
|
||||
@@ -2874,7 +2874,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should be restricted when updating object without addField permission', async done => {
|
||||
it_id('51e896e9-73b3-404f-b5ff-bdb99005a9f7')(it)('should be restricted when updating object without addField permission', async done => {
|
||||
await updateCLP({
|
||||
update: {
|
||||
'*': true,
|
||||
@@ -2946,7 +2946,7 @@ describe('Pointer Permissions', () => {
|
||||
await initialize();
|
||||
});
|
||||
|
||||
it('should not limit the scope of grouped read permissions', async done => {
|
||||
it_id('b43db366-8cce-4a11-9cf2-eeee9603d40b')(it)('should not limit the scope of grouped read permissions', async done => {
|
||||
await updateCLP({
|
||||
get: {
|
||||
pointerFields: ['owner'],
|
||||
@@ -2967,7 +2967,7 @@ describe('Pointer Permissions', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should not limit the scope of grouped write permissions', async done => {
|
||||
it_id('bbb1686d-0e2a-4365-8b64-b5faa3e7b9cf')(it)('should not limit the scope of grouped write permissions', async done => {
|
||||
await updateCLP({
|
||||
update: {
|
||||
pointerFields: ['owner'],
|
||||
|
||||
@@ -168,7 +168,7 @@ describe('PushController', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('properly increment badges', async () => {
|
||||
it_id('01e3e1b8-fad2-4249-b664-5a3efaab8cb1')(it)('properly increment badges', async () => {
|
||||
const pushAdapter = {
|
||||
send: function (body, installations) {
|
||||
const badge = body.data.badge;
|
||||
@@ -233,7 +233,7 @@ describe('PushController', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('properly increment badges by more than 1', async () => {
|
||||
it_id('14afcedf-e65d-41cd-981e-07f32df84c14')(it)('properly increment badges by more than 1', async () => {
|
||||
const pushAdapter = {
|
||||
send: function (body, installations) {
|
||||
const badge = body.data.badge;
|
||||
@@ -295,7 +295,7 @@ describe('PushController', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('properly set badges to 1', async () => {
|
||||
it_id('758dd579-aa91-4010-9033-8d48d3463644')(it)('properly set badges to 1', async () => {
|
||||
const pushAdapter = {
|
||||
send: function (body, installations) {
|
||||
const badge = body.data.badge;
|
||||
@@ -350,7 +350,7 @@ describe('PushController', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('properly set badges to 1 with complex query #2903 #3022', async () => {
|
||||
it_id('75c39ae3-06ac-4354-b321-931e81c5a927')(it)('properly set badges to 1 with complex query #2903 #3022', async () => {
|
||||
const payload = {
|
||||
data: {
|
||||
alert: 'Hello World!',
|
||||
@@ -406,7 +406,7 @@ describe('PushController', () => {
|
||||
expect(results.length).toBe(5);
|
||||
});
|
||||
|
||||
it('properly creates _PushStatus', async () => {
|
||||
it_id('667f31c0-b458-4f61-ab57-668c04e3cc0b')(it)('properly creates _PushStatus', async () => {
|
||||
const pushStatusAfterSave = {
|
||||
handler: function () {},
|
||||
};
|
||||
@@ -521,7 +521,7 @@ describe('PushController', () => {
|
||||
expect(succeedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('properly creates _PushStatus without serverURL', async () => {
|
||||
it_id('30e0591a-56de-4720-8c60-7d72291b532a')(it)('properly creates _PushStatus without serverURL', async () => {
|
||||
const pushStatusAfterSave = {
|
||||
handler: function () {},
|
||||
};
|
||||
@@ -615,7 +615,7 @@ describe('PushController', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should support full RESTQuery for increment', async () => {
|
||||
it_id('53551fc3-b975-4774-92e6-7e5f3c05e105')(it)('should support full RESTQuery for increment', async () => {
|
||||
const payload = {
|
||||
data: {
|
||||
alert: 'Hello World!',
|
||||
@@ -1030,7 +1030,7 @@ describe('PushController', () => {
|
||||
// No installation is in es so only 1 call for fr, and another for default
|
||||
});
|
||||
|
||||
it('should update audiences', async () => {
|
||||
it_id('ef2e5569-50c3-40c2-ab49-175cdbd5f024')(it)('should update audiences', async () => {
|
||||
const pushAdapter = {
|
||||
send: function (body, installations) {
|
||||
return successfulTransmissions(body, installations);
|
||||
|
||||
@@ -271,7 +271,7 @@ describe('PushWorker', () => {
|
||||
toAwait.then(done).catch(done);
|
||||
});
|
||||
|
||||
it('tracks push status per UTC offsets', done => {
|
||||
it_id('764d28ab-241b-4b96-8ce9-e03541850e3f')(it)('tracks push status per UTC offsets', done => {
|
||||
const config = Config.get('test');
|
||||
const handler = pushStatusHandler(config);
|
||||
const spy = spyOn(rest, 'update').and.callThrough();
|
||||
|
||||
@@ -16,8 +16,13 @@ const emailAdapter = {
|
||||
const appName = 'test';
|
||||
const publicServerURL = 'http://localhost:8378/1';
|
||||
|
||||
describe('Regex Vulnerabilities', function () {
|
||||
beforeEach(async function () {
|
||||
describe('Regex Vulnerabilities', () => {
|
||||
let objectId;
|
||||
let sessionToken;
|
||||
let partialSessionToken;
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
await reconfigureServer({
|
||||
maintenanceKey: 'test2',
|
||||
verifyUserEmails: true,
|
||||
@@ -38,13 +43,13 @@ describe('Regex Vulnerabilities', function () {
|
||||
email: 'someemail@somedomain.com',
|
||||
}),
|
||||
});
|
||||
this.objectId = signUpResponse.data.objectId;
|
||||
this.sessionToken = signUpResponse.data.sessionToken;
|
||||
this.partialSessionToken = this.sessionToken.slice(0, 3);
|
||||
objectId = signUpResponse.data.objectId;
|
||||
sessionToken = signUpResponse.data.sessionToken;
|
||||
partialSessionToken = sessionToken.slice(0, 3);
|
||||
});
|
||||
|
||||
describe('on session token', function () {
|
||||
it('should not work with regex', async function () {
|
||||
describe('on session token', () => {
|
||||
it('should not work with regex', async () => {
|
||||
try {
|
||||
await request({
|
||||
url: `${serverURL}/users/me`,
|
||||
@@ -53,7 +58,7 @@ describe('Regex Vulnerabilities', function () {
|
||||
body: JSON.stringify({
|
||||
...keys,
|
||||
_SessionToken: {
|
||||
$regex: this.partialSessionToken,
|
||||
$regex: partialSessionToken,
|
||||
},
|
||||
_method: 'GET',
|
||||
}),
|
||||
@@ -65,43 +70,43 @@ describe('Regex Vulnerabilities', function () {
|
||||
}
|
||||
});
|
||||
|
||||
it('should work with plain token', async function () {
|
||||
it('should work with plain token', async () => {
|
||||
const meResponse = await request({
|
||||
url: `${serverURL}/users/me`,
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
...keys,
|
||||
_SessionToken: this.sessionToken,
|
||||
_SessionToken: sessionToken,
|
||||
_method: 'GET',
|
||||
}),
|
||||
});
|
||||
expect(meResponse.data.objectId).toEqual(this.objectId);
|
||||
expect(meResponse.data.sessionToken).toEqual(this.sessionToken);
|
||||
expect(meResponse.data.objectId).toEqual(objectId);
|
||||
expect(meResponse.data.sessionToken).toEqual(sessionToken);
|
||||
});
|
||||
});
|
||||
|
||||
describe('on verify e-mail', function () {
|
||||
describe('on verify e-mail', () => {
|
||||
beforeEach(async function () {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
this.user = await userQuery.get(this.objectId, { useMasterKey: true });
|
||||
user = await userQuery.get(objectId, { useMasterKey: true });
|
||||
});
|
||||
|
||||
it('should not work with regex', async function () {
|
||||
expect(this.user.get('emailVerified')).toEqual(false);
|
||||
it('should not work with regex', async () => {
|
||||
expect(user.get('emailVerified')).toEqual(false);
|
||||
await request({
|
||||
url: `${serverURL}/apps/test/verify_email?username=someemail@somedomain.com&token[$regex]=`,
|
||||
method: 'GET',
|
||||
});
|
||||
await this.user.fetch({ useMasterKey: true });
|
||||
expect(this.user.get('emailVerified')).toEqual(false);
|
||||
await user.fetch({ useMasterKey: true });
|
||||
expect(user.get('emailVerified')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should work with plain token', async function () {
|
||||
expect(this.user.get('emailVerified')).toEqual(false);
|
||||
it_id('92bbb86d-bcda-49fa-8d79-aa0501078044')(it)('should work with plain token', async () => {
|
||||
expect(user.get('emailVerified')).toEqual(false);
|
||||
const current = await request({
|
||||
method: 'GET',
|
||||
url: `http://localhost:8378/1/classes/_User/${this.user.id}`,
|
||||
url: `http://localhost:8378/1/classes/_User/${user.id}`,
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
@@ -115,18 +120,18 @@ describe('Regex Vulnerabilities', function () {
|
||||
url: `${serverURL}/apps/test/verify_email?username=someemail@somedomain.com&token=${current._email_verify_token}`,
|
||||
method: 'GET',
|
||||
});
|
||||
await this.user.fetch({ useMasterKey: true });
|
||||
expect(this.user.get('emailVerified')).toEqual(true);
|
||||
await user.fetch({ useMasterKey: true });
|
||||
expect(user.get('emailVerified')).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('on password reset', function () {
|
||||
beforeEach(async function () {
|
||||
this.user = await Parse.User.logIn('someemail@somedomain.com', 'somepassword');
|
||||
describe('on password reset', () => {
|
||||
beforeEach(async () => {
|
||||
user = await Parse.User.logIn('someemail@somedomain.com', 'somepassword');
|
||||
});
|
||||
|
||||
it('should not work with regex', async function () {
|
||||
expect(this.user.id).toEqual(this.objectId);
|
||||
it('should not work with regex', async () => {
|
||||
expect(user.id).toEqual(objectId);
|
||||
await request({
|
||||
url: `${serverURL}/requestPasswordReset`,
|
||||
method: 'POST',
|
||||
@@ -137,7 +142,7 @@ describe('Regex Vulnerabilities', function () {
|
||||
email: 'someemail@somedomain.com',
|
||||
}),
|
||||
});
|
||||
await this.user.fetch({ useMasterKey: true });
|
||||
await user.fetch({ useMasterKey: true });
|
||||
const passwordResetResponse = await request({
|
||||
url: `${serverURL}/apps/test/request_password_reset?username=someemail@somedomain.com&token[$regex]=`,
|
||||
method: 'GET',
|
||||
@@ -162,8 +167,8 @@ describe('Regex Vulnerabilities', function () {
|
||||
}
|
||||
});
|
||||
|
||||
it('should work with plain token', async function () {
|
||||
expect(this.user.id).toEqual(this.objectId);
|
||||
it('should work with plain token', async () => {
|
||||
expect(user.id).toEqual(objectId);
|
||||
await request({
|
||||
url: `${serverURL}/requestPasswordReset`,
|
||||
method: 'POST',
|
||||
@@ -176,7 +181,7 @@ describe('Regex Vulnerabilities', function () {
|
||||
});
|
||||
const current = await request({
|
||||
method: 'GET',
|
||||
url: `http://localhost:8378/1/classes/_User/${this.user.id}`,
|
||||
url: `http://localhost:8378/1/classes/_User/${user.id}`,
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
@@ -204,7 +209,7 @@ describe('Regex Vulnerabilities', function () {
|
||||
},
|
||||
});
|
||||
const userAgain = await Parse.User.logIn('someemail@somedomain.com', 'newpassword');
|
||||
expect(userAgain.id).toEqual(this.objectId);
|
||||
expect(userAgain.id).toEqual(objectId);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -392,7 +392,7 @@ describe('RestQuery.each', () => {
|
||||
beforeEach(() => {
|
||||
config = Config.get('test');
|
||||
});
|
||||
it('should run each', async () => {
|
||||
it_id('3416c90b-ee2e-4bb5-9231-46cd181cd0a2')(it)('should run each', async () => {
|
||||
const objects = [];
|
||||
while (objects.length != 10) {
|
||||
objects.push(new Parse.Object('Object', { value: objects.length }));
|
||||
@@ -419,7 +419,7 @@ describe('RestQuery.each', () => {
|
||||
expect(results.length).toBe(7);
|
||||
});
|
||||
|
||||
it('should work with query on relations', async () => {
|
||||
it_id('0fe22501-4b18-461e-b87d-82ceac4a496e')(it)('should work with query on relations', async () => {
|
||||
const objectA = new Parse.Object('Letter', { value: 'A' });
|
||||
const objectB = new Parse.Object('Letter', { value: 'B' });
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ describe('Schema Performance', function () {
|
||||
expect(getAllSpy.calls.count()).toBe(2);
|
||||
});
|
||||
|
||||
it('does reload with schemaCacheTtl', async () => {
|
||||
it_id('9dd70965-b683-4cb8-b43a-44c1f4def9f4')(it)('does reload with schemaCacheTtl', async () => {
|
||||
const databaseURI =
|
||||
process.env.PARSE_SERVER_TEST_DB === 'postgres'
|
||||
? process.env.PARSE_SERVER_TEST_DATABASE_URI
|
||||
@@ -241,7 +241,7 @@ describe('Schema Performance', function () {
|
||||
expect(spy.reloadCalls).toBe(1);
|
||||
});
|
||||
|
||||
it('cannot set invalid databaseOptions', async () => {
|
||||
it_id('b0ae21f2-c947-48ed-a0db-e8900d45a4c8')(it)('cannot set invalid databaseOptions', async () => {
|
||||
const expectError = async (key, value, expected) =>
|
||||
expectAsync(
|
||||
reconfigureServer({ databaseAdapter: undefined, databaseOptions: { [key]: value } })
|
||||
|
||||
@@ -68,7 +68,7 @@ describe('Uniqueness', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('fails when attempting to ensure uniqueness of fields that are not currently unique', done => {
|
||||
it_id('802650a9-a6db-447e-88d0-8aae99100088')(it)('fails when attempting to ensure uniqueness of fields that are not currently unique', done => {
|
||||
const o1 = new Parse.Object('UniqueFail');
|
||||
o1.set('key', 'val');
|
||||
const o2 = new Parse.Object('UniqueFail');
|
||||
|
||||
@@ -5,7 +5,7 @@ const Auth = require('../lib/Auth');
|
||||
describe('UserController', () => {
|
||||
describe('sendVerificationEmail', () => {
|
||||
describe('parseFrameURL not provided', () => {
|
||||
it('uses publicServerURL', async () => {
|
||||
it_id('61338330-eca7-4c33-8816-7ff05966f43b')(it)('uses publicServerURL', async () => {
|
||||
await reconfigureServer({
|
||||
publicServerURL: 'http://www.example.com',
|
||||
customPages: {
|
||||
@@ -40,7 +40,7 @@ describe('UserController', () => {
|
||||
});
|
||||
|
||||
describe('parseFrameURL provided', () => {
|
||||
it('uses parseFrameURL and includes the destination in the link parameter', async () => {
|
||||
it_id('673c2bb1-049e-4dda-b6be-88c866260036')(it)('uses parseFrameURL and includes the destination in the link parameter', async () => {
|
||||
await reconfigureServer({
|
||||
publicServerURL: 'http://www.example.com',
|
||||
customPages: {
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('sends verification email if email verification is enabled', done => {
|
||||
it_id('5e558687-40f3-496c-9e4f-af6100bd1b2f')(it)('sends verification email if email verification is enabled', done => {
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: () => Promise.resolve(),
|
||||
sendPasswordResetEmail: () => Promise.resolve(),
|
||||
@@ -158,7 +158,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('does send with a simple adapter', done => {
|
||||
it_id('33d31119-c724-4f5d-83ec-f56815d23df3')(it)('does send with a simple adapter', done => {
|
||||
let calls = 0;
|
||||
const emailAdapter = {
|
||||
sendMail: function (options) {
|
||||
@@ -305,7 +305,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
|
||||
expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', async () => {
|
||||
it_id('2a5d24be-2ca5-4385-b580-1423bd392e43')(it)('allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', async () => {
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
@@ -381,7 +381,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not allow signup with preventSignupWithUnverified', async () => {
|
||||
it_id('a18a07af-0319-4f15-8237-28070c5948fa')(it)('does not allow signup with preventSignupWithUnverified', async () => {
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
@@ -616,7 +616,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('receives the app name and user in the adapter', done => {
|
||||
it_id('45f550a2-a2b2-4b2b-b533-ccbf96139cc9')(it)('receives the app name and user in the adapter', done => {
|
||||
let emailSent = false;
|
||||
const emailAdapter = {
|
||||
sendVerificationEmail: options => {
|
||||
@@ -644,7 +644,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('when you click the link in the email it sets emailVerified to true and redirects you', done => {
|
||||
it_id('ea37ef62-aad8-4a17-8dfe-35e5b2986f0f')(it)('when you click the link in the email it sets emailVerified to true and redirects you', done => {
|
||||
const user = new Parse.User();
|
||||
let sendEmailOptions;
|
||||
const emailAdapter = {
|
||||
|
||||
@@ -174,7 +174,7 @@ describe_only(() => {
|
||||
describe_only(() => {
|
||||
return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug';
|
||||
})('verbose logs', () => {
|
||||
it('mask sensitive information in _User class', done => {
|
||||
it_id('9ca72994-d255-4c11-a5a2-693c99ee2cdb')(it)('mask sensitive information in _User class', done => {
|
||||
reconfigureServer({ verbose: true })
|
||||
.then(() => createTestUser())
|
||||
.then(() => {
|
||||
|
||||
+27
-39
@@ -1,6 +1,5 @@
|
||||
const batch = require('../lib/batch');
|
||||
const request = require('../lib/request');
|
||||
const TestUtils = require('../lib/TestUtils');
|
||||
|
||||
const originalURL = '/parse/batch';
|
||||
const serverURL = 'http://localhost:1234/parse';
|
||||
@@ -18,9 +17,13 @@ const headers = {
|
||||
};
|
||||
|
||||
describe('batch', () => {
|
||||
let createSpy;
|
||||
beforeEach(async () => {
|
||||
createSpy = spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
});
|
||||
|
||||
it('should return the proper url', () => {
|
||||
const internalURL = batch.makeBatchRoutingPathFunction(originalURL)('/parse/classes/Object');
|
||||
|
||||
expect(internalURL).toEqual('/classes/Object');
|
||||
});
|
||||
|
||||
@@ -133,8 +136,6 @@ describe('batch', () => {
|
||||
});
|
||||
|
||||
it('should handle a batch request without transaction', async () => {
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
|
||||
const response = await request({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
@@ -154,7 +155,6 @@ describe('batch', () => {
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
expect(response.data.length).toEqual(2);
|
||||
expect(response.data[0].success.objectId).toBeDefined();
|
||||
expect(response.data[0].success.createdAt).toBeDefined();
|
||||
@@ -162,15 +162,13 @@ describe('batch', () => {
|
||||
expect(response.data[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
const results = await query.find();
|
||||
expect(databaseAdapter.createObject.calls.count()).toBe(2);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toEqual(null);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(createSpy.calls.count()).toBe(2);
|
||||
expect(createSpy.calls.argsFor(0)[3]).toEqual(null);
|
||||
expect(createSpy.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
});
|
||||
|
||||
it('should handle a batch request with transaction = false', async () => {
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
|
||||
const response = await request({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
@@ -196,11 +194,12 @@ describe('batch', () => {
|
||||
expect(response.data[0].success.createdAt).toBeDefined();
|
||||
expect(response.data[1].success.objectId).toBeDefined();
|
||||
expect(response.data[1].success.createdAt).toBeDefined();
|
||||
|
||||
const query = new Parse.Query('MyObject');
|
||||
const results = await query.find();
|
||||
expect(databaseAdapter.createObject.calls.count()).toBe(2);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toEqual(null);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(createSpy.calls.count()).toBe(2);
|
||||
expect(createSpy.calls.argsFor(0)[3]).toEqual(null);
|
||||
expect(createSpy.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
});
|
||||
|
||||
@@ -209,24 +208,11 @@ describe('batch', () => {
|
||||
process.env.PARSE_SERVER_TEST_DB === 'postgres'
|
||||
) {
|
||||
describe('transactions', () => {
|
||||
beforeEach(async () => {
|
||||
await TestUtils.destroyAllDataPermanently(true);
|
||||
if (process.env.MONGODB_TOPOLOGY === 'replicaset') {
|
||||
await reconfigureServer({
|
||||
databaseAdapter: undefined,
|
||||
databaseURI:
|
||||
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
|
||||
});
|
||||
} else {
|
||||
await reconfigureServer();
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle a batch request with transaction = true', async () => {
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject.save();
|
||||
await myObject.destroy();
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
createSpy.calls.reset();
|
||||
const response = await request({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
@@ -254,10 +240,10 @@ describe('batch', () => {
|
||||
expect(response.data[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
const results = await query.find();
|
||||
expect(databaseAdapter.createObject.calls.count() % 2).toBe(0);
|
||||
for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) {
|
||||
expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe(
|
||||
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
|
||||
expect(createSpy.calls.count()).toBe(2);
|
||||
for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) {
|
||||
expect(createSpy.calls.argsFor(i)[3]).toBe(
|
||||
createSpy.calls.argsFor(i + 1)[3]
|
||||
);
|
||||
}
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
@@ -265,9 +251,11 @@ describe('batch', () => {
|
||||
|
||||
it('should not save anything when one operation fails in a transaction', async () => {
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject.save();
|
||||
await myObject.save({ key: 'stringField' });
|
||||
await myObject.destroy();
|
||||
createSpy.calls.reset();
|
||||
try {
|
||||
// Saving a number to a string field should fail
|
||||
await request({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
@@ -380,20 +368,20 @@ describe('batch', () => {
|
||||
it('should generate separate session for each call', async () => {
|
||||
await reconfigureServer();
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject.save();
|
||||
await myObject.save({ key: 'stringField' });
|
||||
await myObject.destroy();
|
||||
|
||||
const myObject2 = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject2.save();
|
||||
await myObject2.save({ key: 'stringField' });
|
||||
await myObject2.destroy();
|
||||
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
createSpy.calls.reset();
|
||||
|
||||
let myObjectCalls = 0;
|
||||
Parse.Cloud.beforeSave('MyObject', async () => {
|
||||
myObjectCalls++;
|
||||
if (myObjectCalls === 2) {
|
||||
try {
|
||||
// Saving a number to a string field should fail
|
||||
await request({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
@@ -560,14 +548,14 @@ describe('batch', () => {
|
||||
const results3 = await query3.find();
|
||||
expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
|
||||
expect(databaseAdapter.createObject.calls.count() >= 13).toEqual(true);
|
||||
expect(createSpy.calls.count() >= 13).toEqual(true);
|
||||
let transactionalSession;
|
||||
let transactionalSession2;
|
||||
let myObjectDBCalls = 0;
|
||||
let myObject2DBCalls = 0;
|
||||
let myObject3DBCalls = 0;
|
||||
for (let i = 0; i < databaseAdapter.createObject.calls.count(); i++) {
|
||||
const args = databaseAdapter.createObject.calls.argsFor(i);
|
||||
for (let i = 0; i < createSpy.calls.count(); i++) {
|
||||
const args = createSpy.calls.argsFor(i);
|
||||
switch (args[0]) {
|
||||
case 'MyObject':
|
||||
myObjectDBCalls++;
|
||||
|
||||
+14
-8
@@ -14,6 +14,7 @@ if (dns.setDefaultResultOrder) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
|
||||
jasmine.getEnv().addReporter(new CurrentSpecReporter());
|
||||
jasmine.getEnv().addReporter(new SpecReporter());
|
||||
global.retryFlakyTests();
|
||||
|
||||
global.on_db = (db, callback, elseCallback) => {
|
||||
if (process.env.PARSE_SERVER_TEST_DB == db) {
|
||||
@@ -287,7 +288,7 @@ afterEach(function (done) {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
global.displaySlowTests();
|
||||
global.displayTestStats();
|
||||
});
|
||||
|
||||
const TestObject = Parse.Object.extend({
|
||||
@@ -466,13 +467,18 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
// Disable test if its UUID is found in testExclusionList
|
||||
global.it_id = (id, func) => {
|
||||
if (testExclusionList.includes(id)) {
|
||||
return xit;
|
||||
} else {
|
||||
return func || it;
|
||||
}
|
||||
/**
|
||||
* Assign ID to test and run it. Disable test if its UUID is found in testExclusionList.
|
||||
* @param {String} id The UUID of the test.
|
||||
*/
|
||||
global.it_id = id => {
|
||||
return testFunc => {
|
||||
if (testExclusionList.includes(id)) {
|
||||
return xit;
|
||||
} else {
|
||||
return testFunc;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
global.it_only_db = db => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const {
|
||||
numberParser,
|
||||
numberOrBoolParser,
|
||||
numberOrStringParser,
|
||||
booleanParser,
|
||||
objectParser,
|
||||
arrayParser,
|
||||
@@ -18,6 +19,15 @@ describe('parsers', () => {
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('parses correctly with numberOrStringParser', () => {
|
||||
const parser = numberOrStringParser('key');
|
||||
expect(parser('100d')).toEqual('100d');
|
||||
expect(parser(100)).toEqual(100);
|
||||
expect(() => {
|
||||
parser(undefined);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('parses correctly with numberOrBoolParser', () => {
|
||||
const parser = numberOrBoolParser('key');
|
||||
expect(parser(true)).toEqual(true);
|
||||
|
||||
+2
-2
@@ -247,7 +247,7 @@ describe('rest create', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('handles array, object, date', done => {
|
||||
it_id('6c30306f-328c-47f2-88a7-2deffaee997f')(it)('handles array, object, date', done => {
|
||||
const now = new Date();
|
||||
const obj = {
|
||||
array: [1, 2, 3],
|
||||
@@ -787,7 +787,7 @@ describe('rest create', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot get object in _GlobalConfig if not masterKey through pointer', async () => {
|
||||
it_id('3ce563bf-93aa-4d0b-9af9-c5fb246ac9fc')(it)('cannot get object in _GlobalConfig if not masterKey through pointer', async () => {
|
||||
await Parse.Config.save({ privateData: 'secret' }, { privateData: true });
|
||||
const obj2 = new Parse.Object('TestObject');
|
||||
obj2.set('globalConfigPointer', {
|
||||
|
||||
@@ -3650,7 +3650,7 @@ describe('schemas', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('get indexes on startup', done => {
|
||||
it_id('5d0926b2-2d31-459d-a2b1-23ecc32e72a3')(it_exclude_dbs(['postgres']))('get indexes on startup', done => {
|
||||
const obj = new Parse.Object('TestObject');
|
||||
obj
|
||||
.save()
|
||||
@@ -3673,7 +3673,7 @@ describe('schemas', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('get compound indexes on startup', done => {
|
||||
it_id('9f2ba51a-6a9c-4b25-9da0-51c82ac65f90')(it_exclude_dbs(['postgres']))('get compound indexes on startup', done => {
|
||||
const obj = new Parse.Object('TestObject');
|
||||
obj.set('subject', 'subject');
|
||||
obj.set('comment', 'comment');
|
||||
@@ -3708,7 +3708,7 @@ describe('schemas', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it_exclude_dbs(['postgres'])('cannot update to duplicate value on unique index', done => {
|
||||
it_id('cbd5d897-b938-43a4-8f5a-5d02dd2be9be')(it_exclude_dbs(['postgres']))('cannot update to duplicate value on unique index', done => {
|
||||
const index = {
|
||||
code: 1,
|
||||
};
|
||||
|
||||
@@ -1,13 +1,36 @@
|
||||
// Sets a global variable to the current test spec
|
||||
// ex: global.currentSpec.description
|
||||
const { performance } = require('perf_hooks');
|
||||
|
||||
global.currentSpec = null;
|
||||
|
||||
const timerMap = {};
|
||||
const duplicates = [];
|
||||
/**
|
||||
* Names of tests that fail randomly and are considered flaky. These tests will be retried
|
||||
* a number of times to reduce the chance of false negatives. The test name must be the same
|
||||
* as the one displayed in the CI log test output.
|
||||
*/
|
||||
const flakyTests = [
|
||||
// Timeout
|
||||
"ParseLiveQuery handle invalid websocket payload length",
|
||||
// Unhandled promise rejection: TypeError: message.split is not a function
|
||||
"rest query query internal field",
|
||||
// TypeError: Cannot read properties of undefined (reading 'link')
|
||||
"UserController sendVerificationEmail parseFrameURL not provided uses publicServerURL",
|
||||
// TypeError: Cannot read properties of undefined (reading 'link')
|
||||
"UserController sendVerificationEmail parseFrameURL provided uses parseFrameURL and includes the destination in the link parameter",
|
||||
// Expected undefined to be defined
|
||||
"Email Verification Token Expiration: sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp",
|
||||
];
|
||||
|
||||
/** The minimum execution time in seconds for a test to be considered slow. */
|
||||
const slowTestLimit = 2;
|
||||
|
||||
/** The number of times to retry a flaky test. */
|
||||
const retries = 5;
|
||||
|
||||
const timerMap = {};
|
||||
const retryMap = {};
|
||||
const duplicates = [];
|
||||
class CurrentSpecReporter {
|
||||
specStarted(spec) {
|
||||
if (timerMap[spec.fullName]) {
|
||||
@@ -26,20 +49,74 @@ class CurrentSpecReporter {
|
||||
global.currentSpec = null;
|
||||
}
|
||||
}
|
||||
global.displaySlowTests = function() {
|
||||
const times = Object.values(timerMap).sort((a,b) => b - a);
|
||||
|
||||
global.displayTestStats = function() {
|
||||
const times = Object.values(timerMap).sort((a,b) => b - a).filter(time => time >= slowTestLimit);
|
||||
if (times.length > 0) {
|
||||
console.log(`Slow tests with execution time >=${slowTestLimit}s:`);
|
||||
}
|
||||
times.forEach((time) => {
|
||||
if (time >= slowTestLimit) {
|
||||
console.warn(`${time.toFixed(1)}s:`, Object.keys(timerMap).find(key => timerMap[key] === time));
|
||||
}
|
||||
console.warn(`${time.toFixed(1)}s:`, Object.keys(timerMap).find(key => timerMap[key] === time));
|
||||
});
|
||||
console.log('\n');
|
||||
duplicates.forEach((spec) => {
|
||||
console.warn('Duplicate spec: ' + spec);
|
||||
});
|
||||
console.log('\n');
|
||||
Object.keys(retryMap).forEach((spec) => {
|
||||
console.warn(`Flaky test: ${spec} failed ${retryMap[spec]} times`);
|
||||
});
|
||||
console.log('\n');
|
||||
};
|
||||
|
||||
global.retryFlakyTests = function() {
|
||||
const originalSpecConstructor = jasmine.Spec;
|
||||
|
||||
jasmine.Spec = function(attrs) {
|
||||
const spec = new originalSpecConstructor(attrs);
|
||||
const originalTestFn = spec.queueableFn.fn;
|
||||
const runOriginalTest = () => {
|
||||
if (originalTestFn.length == 0) {
|
||||
// handle async testing
|
||||
return originalTestFn();
|
||||
} else {
|
||||
// handle done() callback
|
||||
return new Promise((resolve) => {
|
||||
originalTestFn(resolve);
|
||||
});
|
||||
}
|
||||
};
|
||||
spec.queueableFn.fn = async function() {
|
||||
const isFlaky = flakyTests.includes(spec.result.fullName);
|
||||
const runs = isFlaky ? retries : 1;
|
||||
let exceptionCaught;
|
||||
let returnValue;
|
||||
|
||||
for (let i = 0; i < runs; ++i) {
|
||||
spec.result.failedExpectations = [];
|
||||
returnValue = undefined;
|
||||
exceptionCaught = undefined;
|
||||
try {
|
||||
returnValue = await runOriginalTest();
|
||||
} catch (exception) {
|
||||
exceptionCaught = exception;
|
||||
}
|
||||
const failed = !spec.markedPending &&
|
||||
(exceptionCaught || spec.result.failedExpectations.length != 0);
|
||||
if (!failed) {
|
||||
break;
|
||||
}
|
||||
if (isFlaky) {
|
||||
retryMap[spec.result.fullName] = (retryMap[spec.result.fullName] || 0) + 1;
|
||||
}
|
||||
}
|
||||
if (exceptionCaught) {
|
||||
throw exceptionCaught;
|
||||
}
|
||||
return returnValue;
|
||||
};
|
||||
return spec;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = CurrentSpecReporter;
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
"spec_dir": "spec",
|
||||
"spec_files": ["*spec.js"],
|
||||
"helpers": ["helper.js"],
|
||||
"random": false
|
||||
"random": true
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Create Deferred Promise
|
||||
function resolvingPromise() {
|
||||
let res;
|
||||
let rej;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
res = resolve;
|
||||
rej = reject;
|
||||
});
|
||||
promise.resolve = res;
|
||||
promise.reject = rej;
|
||||
return promise;
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
module.exports = { resolvingPromise, sleep };
|
||||
@@ -1,6 +1,51 @@
|
||||
const request = require('../lib/request');
|
||||
|
||||
describe('Vulnerabilities', () => {
|
||||
describe('(GHSA-8xq9-g7ch-35hg) Custom object ID allows to acquire role privilege', () => {
|
||||
beforeAll(async () => {
|
||||
await reconfigureServer({ allowCustomObjectId: true });
|
||||
Parse.allowCustomObjectId = true;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await reconfigureServer({ allowCustomObjectId: false });
|
||||
Parse.allowCustomObjectId = false;
|
||||
});
|
||||
|
||||
it('denies user creation with poisoned object ID', async () => {
|
||||
await expectAsync(
|
||||
new Parse.User({ id: 'role:a', username: 'a', password: '123' }).save()
|
||||
).toBeRejectedWith(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.'));
|
||||
});
|
||||
|
||||
describe('existing sessions for users with poisoned object ID', () => {
|
||||
/** @type {Parse.User} */
|
||||
let poisonedUser;
|
||||
/** @type {Parse.User} */
|
||||
let innocentUser;
|
||||
|
||||
beforeAll(async () => {
|
||||
const parseServer = await global.reconfigureServer();
|
||||
const databaseController = parseServer.config.databaseController;
|
||||
[poisonedUser, innocentUser] = await Promise.all(
|
||||
['role:abc', 'abc'].map(async id => {
|
||||
// Create the users directly on the db to bypass the user creation check
|
||||
await databaseController.create('_User', { objectId: id });
|
||||
// Use the master key to create a session for them to bypass the session check
|
||||
return Parse.User.loginAs(id);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('refuses session token of user with poisoned object ID', async () => {
|
||||
await expectAsync(
|
||||
new Parse.Query(Parse.User).find({ sessionToken: poisonedUser.getSessionToken() })
|
||||
).toBeRejectedWith(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.'));
|
||||
await new Parse.Query(Parse.User).find({ sessionToken: innocentUser.getSessionToken() });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Object prototype pollution', () => {
|
||||
it('denies object prototype to be polluted with keyword "constructor"', async () => {
|
||||
const headers = {
|
||||
@@ -203,7 +248,7 @@ describe('Vulnerabilities', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('denies creating a hook with polluted data', async () => {
|
||||
it_id('e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d')(it)('denies creating a hook with polluted data', async () => {
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const port = 34567;
|
||||
|
||||
@@ -59,9 +59,9 @@ export class FilesAdapter {
|
||||
* @param {Config} config - server configuration
|
||||
* @param {string} filename
|
||||
*
|
||||
* @return {string} Absolute URL
|
||||
* @return {string | Promise<string>} Absolute URL
|
||||
*/
|
||||
getFileLocation(config: Config, filename: string): string {}
|
||||
getFileLocation(config: Config, filename: string): string | Promise<string> {}
|
||||
|
||||
/** Validate a filename for this adapter type
|
||||
*
|
||||
|
||||
@@ -180,6 +180,11 @@ const getAuthForSessionToken = async function ({
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token is expired.');
|
||||
}
|
||||
const obj = session.user;
|
||||
|
||||
if (typeof obj['objectId'] === 'string' && obj['objectId'].startsWith('role:')) {
|
||||
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.');
|
||||
}
|
||||
|
||||
delete obj.password;
|
||||
obj['className'] = '_User';
|
||||
obj['sessionToken'] = sessionToken;
|
||||
|
||||
@@ -15,7 +15,7 @@ export class FilesController extends AdaptableController {
|
||||
return this.adapter.getFileData(filename);
|
||||
}
|
||||
|
||||
createFile(config, filename, data, contentType, options) {
|
||||
async createFile(config, filename, data, contentType, options) {
|
||||
const extname = path.extname(filename);
|
||||
|
||||
const hasExtension = extname.length > 0;
|
||||
@@ -30,13 +30,12 @@ export class FilesController extends AdaptableController {
|
||||
filename = randomHexString(32) + '_' + filename;
|
||||
}
|
||||
|
||||
const location = this.adapter.getFileLocation(config, filename);
|
||||
return this.adapter.createFile(filename, data, contentType, options).then(() => {
|
||||
return Promise.resolve({
|
||||
url: location,
|
||||
name: filename,
|
||||
});
|
||||
});
|
||||
const location = await this.adapter.getFileLocation(config, filename);
|
||||
await this.adapter.createFile(filename, data, contentType, options);
|
||||
return {
|
||||
url: location,
|
||||
name: filename,
|
||||
}
|
||||
}
|
||||
|
||||
deleteFile(config, filename) {
|
||||
@@ -55,9 +54,10 @@ export class FilesController extends AdaptableController {
|
||||
* with the current mount point and app id.
|
||||
* Object may be a single object or list of REST-format objects.
|
||||
*/
|
||||
expandFilesInObject(config, object) {
|
||||
async expandFilesInObject(config, object) {
|
||||
if (object instanceof Array) {
|
||||
object.map(obj => this.expandFilesInObject(config, obj));
|
||||
const promises = object.map(obj => this.expandFilesInObject(config, obj));
|
||||
await Promise.all(promises);
|
||||
return;
|
||||
}
|
||||
if (typeof object !== 'object') {
|
||||
@@ -74,7 +74,7 @@ export class FilesController extends AdaptableController {
|
||||
// all filenames starting with a "-" seperated UUID should be from files.parse.com
|
||||
// all other filenames have been migrated or created from Parse Server
|
||||
if (config.fileKey === undefined) {
|
||||
fileObject['url'] = this.adapter.getFileLocation(config, filename);
|
||||
fileObject['url'] = await this.adapter.getFileLocation(config, filename);
|
||||
} else {
|
||||
if (filename.indexOf('tfss-') === 0) {
|
||||
fileObject['url'] =
|
||||
@@ -83,7 +83,7 @@ export class FilesController extends AdaptableController {
|
||||
fileObject['url'] =
|
||||
'http://files.parse.com/' + config.fileKey + '/' + encodeURIComponent(filename);
|
||||
} else {
|
||||
fileObject['url'] = this.adapter.getFileLocation(config, filename);
|
||||
fileObject['url'] = await this.adapter.getFileLocation(config, filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ module.exports.ParseServerOptions = {
|
||||
env: 'PARSE_SERVER_MAX_LOG_FILES',
|
||||
help:
|
||||
"Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null)",
|
||||
action: parsers.objectParser,
|
||||
action: parsers.numberOrStringParser('maxLogFiles'),
|
||||
},
|
||||
maxUploadSize: {
|
||||
env: 'PARSE_SERVER_MAX_UPLOAD_SIZE',
|
||||
|
||||
@@ -23,6 +23,15 @@ function numberOrBoolParser(key) {
|
||||
};
|
||||
}
|
||||
|
||||
function numberOrStringParser(key) {
|
||||
return function (opt) {
|
||||
if (typeof opt === 'string') {
|
||||
return opt;
|
||||
}
|
||||
return numberParser(key)(opt);
|
||||
};
|
||||
}
|
||||
|
||||
function objectParser(opt) {
|
||||
if (typeof opt == 'object') {
|
||||
return opt;
|
||||
@@ -69,6 +78,7 @@ function nullParser(opt) {
|
||||
module.exports = {
|
||||
numberParser,
|
||||
numberOrBoolParser,
|
||||
numberOrStringParser,
|
||||
nullParser,
|
||||
booleanParser,
|
||||
moduleOrObjectParser,
|
||||
|
||||
+14
-17
@@ -735,7 +735,7 @@ _UnsafeRestQuery.prototype.replaceEquality = function () {
|
||||
|
||||
// Returns a promise for whether it was successful.
|
||||
// Populates this.response with an object that only has 'results'.
|
||||
_UnsafeRestQuery.prototype.runFind = function (options = {}) {
|
||||
_UnsafeRestQuery.prototype.runFind = async function (options = {}) {
|
||||
if (this.findOptions.limit === 0) {
|
||||
this.response = { results: [] };
|
||||
return Promise.resolve();
|
||||
@@ -749,24 +749,21 @@ _UnsafeRestQuery.prototype.runFind = function (options = {}) {
|
||||
if (options.op) {
|
||||
findOptions.op = options.op;
|
||||
}
|
||||
return this.config.database
|
||||
.find(this.className, this.restWhere, findOptions, this.auth)
|
||||
.then(results => {
|
||||
if (this.className === '_User' && !findOptions.explain) {
|
||||
for (var result of results) {
|
||||
this.cleanResultAuthData(result);
|
||||
}
|
||||
}
|
||||
const results = await this.config.database.find(this.className, this.restWhere, findOptions, this.auth);
|
||||
if (this.className === '_User' && !findOptions.explain) {
|
||||
for (var result of results) {
|
||||
this.cleanResultAuthData(result);
|
||||
}
|
||||
}
|
||||
|
||||
this.config.filesController.expandFilesInObject(this.config, results);
|
||||
await this.config.filesController.expandFilesInObject(this.config, results);
|
||||
|
||||
if (this.redirectClassName) {
|
||||
for (var r of results) {
|
||||
r.className = this.redirectClassName;
|
||||
}
|
||||
}
|
||||
this.response = { results: results };
|
||||
});
|
||||
if (this.redirectClassName) {
|
||||
for (var r of results) {
|
||||
r.className = this.redirectClassName;
|
||||
}
|
||||
}
|
||||
this.response = { results: results };
|
||||
};
|
||||
|
||||
// Returns a promise for whether it was successful.
|
||||
|
||||
+3
-3
@@ -322,7 +322,7 @@ RestWrite.prototype.runBeforeLoginTrigger = async function (userData) {
|
||||
const extraData = { className: this.className };
|
||||
|
||||
// Expand file objects
|
||||
this.config.filesController.expandFilesInObject(this.config, userData);
|
||||
await this.config.filesController.expandFilesInObject(this.config, userData);
|
||||
|
||||
const user = triggers.inflate(extraData, userData);
|
||||
|
||||
@@ -1412,10 +1412,10 @@ RestWrite.prototype.handleInstallation = function () {
|
||||
// If we short-circuited the object response - then we need to make sure we expand all the files,
|
||||
// since this might not have a query, meaning it won't return the full result back.
|
||||
// TODO: (nlutsenko) This should die when we move to per-class based controllers on _Session/_User
|
||||
RestWrite.prototype.expandFilesForExistingObjects = function () {
|
||||
RestWrite.prototype.expandFilesForExistingObjects = async function () {
|
||||
// Check whether we have a short-circuited response - only then run expansion.
|
||||
if (this.response && this.response.response) {
|
||||
this.config.filesController.expandFilesInObject(this.config, this.response.response);
|
||||
await this.config.filesController.expandFilesInObject(this.config, this.response.response);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -106,6 +106,13 @@ export class ClassesRouter extends PromiseRouter {
|
||||
}
|
||||
|
||||
handleCreate(req) {
|
||||
if (
|
||||
this.className(req) === '_User' &&
|
||||
typeof req.body?.objectId === 'string' &&
|
||||
req.body.objectId.startsWith('role:')
|
||||
) {
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.');
|
||||
}
|
||||
return rest.create(
|
||||
req.config,
|
||||
req.auth,
|
||||
|
||||
@@ -263,7 +263,7 @@ export class FilesRouter {
|
||||
const { filename } = req.params;
|
||||
// run beforeDeleteFile trigger
|
||||
const file = new Parse.File(filename);
|
||||
file._url = filesController.adapter.getFileLocation(req.config, filename);
|
||||
file._url = await filesController.adapter.getFileLocation(req.config, filename);
|
||||
const fileObject = { file, fileSize: null };
|
||||
await triggers.maybeRunFileTrigger(
|
||||
triggers.Types.beforeDelete,
|
||||
|
||||
@@ -2,6 +2,15 @@
|
||||
import Parse from 'parse/node';
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import * as middleware from '../middlewares';
|
||||
import * as triggers from '../triggers';
|
||||
|
||||
const getConfigFromParams = params => {
|
||||
const config = new Parse.Config();
|
||||
for (const attr in params) {
|
||||
config.attributes[attr] = Parse._decode(undefined, params[attr]);
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
||||
export class GlobalConfigRouter extends PromiseRouter {
|
||||
getGlobalConfig(req) {
|
||||
@@ -30,7 +39,7 @@ export class GlobalConfigRouter extends PromiseRouter {
|
||||
});
|
||||
}
|
||||
|
||||
updateGlobalConfig(req) {
|
||||
async updateGlobalConfig(req) {
|
||||
if (req.auth.isReadOnly) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OPERATION_FORBIDDEN,
|
||||
@@ -45,9 +54,37 @@ export class GlobalConfigRouter extends PromiseRouter {
|
||||
acc[`masterKeyOnly.${key}`] = masterKeyOnly[key] || false;
|
||||
return acc;
|
||||
}, {});
|
||||
return req.config.database
|
||||
.update('_GlobalConfig', { objectId: '1' }, update, { upsert: true })
|
||||
.then(() => ({ response: { result: true } }));
|
||||
const className = triggers.getClassName(Parse.Config);
|
||||
const hasBeforeSaveHook = triggers.triggerExists(className, triggers.Types.beforeSave, req.config.applicationId);
|
||||
const hasAfterSaveHook = triggers.triggerExists(className, triggers.Types.afterSave, req.config.applicationId);
|
||||
let originalConfigObject;
|
||||
let updatedConfigObject;
|
||||
const configObject = new Parse.Config();
|
||||
configObject.attributes = params;
|
||||
|
||||
const results = await req.config.database.find('_GlobalConfig', { objectId: '1' }, { limit: 1 });
|
||||
const isNew = results.length !== 1;
|
||||
if (!isNew && (hasBeforeSaveHook || hasAfterSaveHook)) {
|
||||
originalConfigObject = getConfigFromParams(results[0].params);
|
||||
}
|
||||
try {
|
||||
await triggers.maybeRunGlobalConfigTrigger(triggers.Types.beforeSave, req.auth, configObject, originalConfigObject, req.config, req.context);
|
||||
if (isNew) {
|
||||
await req.config.database.update('_GlobalConfig', { objectId: '1' }, update, { upsert: true }, true)
|
||||
updatedConfigObject = configObject;
|
||||
} else {
|
||||
const result = await req.config.database.update('_GlobalConfig', { objectId: '1' }, update, {}, true);
|
||||
updatedConfigObject = getConfigFromParams(result.params);
|
||||
}
|
||||
await triggers.maybeRunGlobalConfigTrigger(triggers.Types.afterSave, req.auth, updatedConfigObject, originalConfigObject, req.config, req.context);
|
||||
return { response: { result: true } }
|
||||
} catch (err) {
|
||||
const error = triggers.resolveError(err, {
|
||||
code: Parse.Error.SCRIPT_FAILED,
|
||||
message: 'Script failed. Unknown error.',
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
mountRoutes() {
|
||||
|
||||
@@ -264,7 +264,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
// Remove hidden properties.
|
||||
UsersRouter.removeHiddenProperties(user);
|
||||
|
||||
req.config.filesController.expandFilesInObject(req.config, user);
|
||||
await req.config.filesController.expandFilesInObject(req.config, user);
|
||||
|
||||
// Before login trigger; throws if failure
|
||||
await maybeRunTrigger(
|
||||
|
||||
@@ -26,3 +26,19 @@ export function destroyAllDataPermanently(fast) {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function resolvingPromise() {
|
||||
let res;
|
||||
let rej;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
res = resolve;
|
||||
rej = reject;
|
||||
});
|
||||
promise.resolve = res;
|
||||
promise.reject = rej;
|
||||
return promise;
|
||||
}
|
||||
|
||||
export function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
@@ -79,10 +79,14 @@ const getRoute = parseClass => {
|
||||
_User: 'users',
|
||||
_Session: 'sessions',
|
||||
'@File': 'files',
|
||||
'@Config' : 'config',
|
||||
}[parseClass] || 'classes';
|
||||
if (parseClass === '@File') {
|
||||
return `/${route}/:id?(.*)`;
|
||||
}
|
||||
if (parseClass === '@Config') {
|
||||
return `/${route}`;
|
||||
}
|
||||
return `/${route}/${parseClass}/:id?(.*)`;
|
||||
};
|
||||
/** @namespace
|
||||
|
||||
@@ -1027,3 +1027,38 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth)
|
||||
}
|
||||
return fileObject;
|
||||
}
|
||||
|
||||
export async function maybeRunGlobalConfigTrigger(triggerType, auth, configObject, originalConfigObject, config, context) {
|
||||
const GlobalConfigClassName = getClassName(Parse.Config);
|
||||
const configTrigger = getTrigger(GlobalConfigClassName, triggerType, config.applicationId);
|
||||
if (typeof configTrigger === 'function') {
|
||||
try {
|
||||
const request = getRequestObject(triggerType, auth, configObject, originalConfigObject, config, context);
|
||||
await maybeRunValidator(request, `${triggerType}.${GlobalConfigClassName}`, auth);
|
||||
if (request.skipWithMasterKey) {
|
||||
return configObject;
|
||||
}
|
||||
const result = await configTrigger(request);
|
||||
logTriggerSuccessBeforeHook(
|
||||
triggerType,
|
||||
'Parse.Config',
|
||||
configObject,
|
||||
result,
|
||||
auth,
|
||||
config.logLevels.triggerBeforeSuccess
|
||||
);
|
||||
return result || configObject;
|
||||
} catch (error) {
|
||||
logTriggerErrorBeforeHook(
|
||||
triggerType,
|
||||
'Parse.Config',
|
||||
configObject,
|
||||
auth,
|
||||
error,
|
||||
config.logLevels.triggerBeforeError
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return configObject;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user