Merge remote-tracking branch 'upstream/alpha' into alpha
This commit is contained in:
@@ -1,3 +1,10 @@
|
|||||||
|
# [7.1.0-alpha.5](https://github.com/parse-community/parse-server/compare/7.1.0-alpha.4...7.1.0-alpha.5) (2024-04-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Prevent Parse Server start in case of unknown option in server configuration ([#8987](https://github.com/parse-community/parse-server/issues/8987)) ([8758e6a](https://github.com/parse-community/parse-server/commit/8758e6abb9dbb68757bddcbd332ad25100c24a0e))
|
||||||
|
|
||||||
# [7.1.0-alpha.4](https://github.com/parse-community/parse-server/compare/7.1.0-alpha.3...7.1.0-alpha.4) (2024-03-31)
|
# [7.1.0-alpha.4](https://github.com/parse-community/parse-server/compare/7.1.0-alpha.3...7.1.0-alpha.4) (2024-03-31)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
"template": "./node_modules/clean-jsdoc-theme",
|
"template": "./node_modules/clean-jsdoc-theme",
|
||||||
"theme_opts": {
|
"theme_opts": {
|
||||||
"default_theme": "dark",
|
"default_theme": "dark",
|
||||||
"title": "<img src='../.github/parse-server-logo.png' class='logo'/>",
|
"title": "<img src='https://raw.githubusercontent.com/parse-community/parse-server/alpha/.github/parse-server-logo.png' class='logo'/>",
|
||||||
"create_style": "header, .sidebar-section-title, .sidebar-title { color: #139cee !important } .logo { margin-left : 40px; margin-right: 40px }"
|
"create_style": "header, .sidebar-section-title, .sidebar-title { color: #139cee !important } .logo { margin-left : 40px; margin-right: 40px }"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
62
package-lock.json
generated
62
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "parse-server",
|
"name": "parse-server",
|
||||||
"version": "7.1.0-alpha.4",
|
"version": "7.1.0-alpha.5",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "parse-server",
|
"name": "parse-server",
|
||||||
"version": "7.1.0-alpha.4",
|
"version": "7.1.0-alpha.5",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"@graphql-tools/merge": "9.0.3",
|
"@graphql-tools/merge": "9.0.3",
|
||||||
"@graphql-tools/schema": "10.0.3",
|
"@graphql-tools/schema": "10.0.3",
|
||||||
"@graphql-tools/utils": "8.12.0",
|
"@graphql-tools/utils": "8.12.0",
|
||||||
"@parse/fs-files-adapter": "2.0.1",
|
"@parse/fs-files-adapter": "3.0.0",
|
||||||
"@parse/push-adapter": "6.0.0",
|
"@parse/push-adapter": "6.0.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"follow-redirects": "1.15.6",
|
"follow-redirects": "1.15.6",
|
||||||
"graphql": "16.8.1",
|
"graphql": "16.8.1",
|
||||||
"graphql-list-fields": "2.0.4",
|
"graphql-list-fields": "2.0.4",
|
||||||
"graphql-relay": "0.10.0",
|
"graphql-relay": "0.10.1",
|
||||||
"graphql-tag": "2.12.6",
|
"graphql-tag": "2.12.6",
|
||||||
"graphql-upload": "15.0.2",
|
"graphql-upload": "15.0.2",
|
||||||
"intersect": "1.0.1",
|
"intersect": "1.0.1",
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/core": "1.10.1",
|
"@actions/core": "1.10.1",
|
||||||
"@apollo/client": "3.9.5",
|
"@apollo/client": "3.9.11",
|
||||||
"@babel/cli": "7.23.9",
|
"@babel/cli": "7.23.9",
|
||||||
"@babel/core": "7.24.3",
|
"@babel/core": "7.24.3",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "7.10.0",
|
"@babel/plugin-proposal-object-rest-spread": "7.10.0",
|
||||||
@@ -161,9 +161,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@apollo/client": {
|
"node_modules/@apollo/client": {
|
||||||
"version": "3.9.5",
|
"version": "3.9.11",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.9.5.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.9.11.tgz",
|
||||||
"integrity": "sha512-7y+c8MTPU+hhTwvcGVtMMGIgWduzrvG1mz5yJMRyqYbheBkkky3Lki6ADWVSBXG1lZoOtPYvB2zDgVfKb2HSsw==",
|
"integrity": "sha512-H7e9m7cRcFO93tokwzqrsbnfKorkpV24xU30hFH5u2g6B+c1DMo/ouyF/YrBPdrTzqxQCjTUmds/FLmJ7626GA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@graphql-typed-document-node/core": "^3.1.1",
|
"@graphql-typed-document-node/core": "^3.1.1",
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
"hoist-non-react-statics": "^3.3.2",
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
"optimism": "^0.18.0",
|
"optimism": "^0.18.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"rehackt": "0.0.5",
|
"rehackt": "0.0.6",
|
||||||
"response-iterator": "^0.2.6",
|
"response-iterator": "^0.2.6",
|
||||||
"symbol-observable": "^4.0.0",
|
"symbol-observable": "^4.0.0",
|
||||||
"ts-invariant": "^0.10.3",
|
"ts-invariant": "^0.10.3",
|
||||||
@@ -3713,9 +3713,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@parse/fs-files-adapter": {
|
"node_modules/@parse/fs-files-adapter": {
|
||||||
"version": "2.0.1",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-3.0.0.tgz",
|
||||||
"integrity": "sha512-9DY0T9lK73Ysw+wxxsBt9rpxWxJpMlHl/fTW175XSajusW0ZP5jERI3BTKeclV28eVmSU690EO2vnwCURsPZ7g=="
|
"integrity": "sha512-Bb+qLtXQ/1SA2Ck6JLVhfD9JQf6cCwgeDZZJjcIdHzUtdPTFu1hj51xdD7tUCL47Ed2i3aAx6K/M6AjLWYVs3A=="
|
||||||
},
|
},
|
||||||
"node_modules/@parse/node-apn": {
|
"node_modules/@parse/node-apn": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
@@ -8824,9 +8824,9 @@
|
|||||||
"integrity": "sha512-q3prnhAL/dBsD+vaGr83B8DzkBijg+Yh+lbt7qp2dW1fpuO+q/upzDXvFJstVsSAA8m11MHGkSxxyxXeLou4MA=="
|
"integrity": "sha512-q3prnhAL/dBsD+vaGr83B8DzkBijg+Yh+lbt7qp2dW1fpuO+q/upzDXvFJstVsSAA8m11MHGkSxxyxXeLou4MA=="
|
||||||
},
|
},
|
||||||
"node_modules/graphql-relay": {
|
"node_modules/graphql-relay": {
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.1.tgz",
|
||||||
"integrity": "sha512-44yBuw2/DLNEiMypbNZBt1yMDbBmyVPVesPywnteGGALiBmdyy1JP8jSg8ClLePg8ZZxk0O4BLhd1a6U/1jDOQ==",
|
"integrity": "sha512-8AtwSe6B0/b4+YzynHr38PP7S+zX5Vs5LEo0BEzGCPq/THAiHa5H5ZLf3bRbKbok15ADxDQSsGJmlqXeJDDPIw==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || ^14.15.0 || >= 15.9.0"
|
"node": "^12.20.0 || ^14.15.0 || >= 15.9.0"
|
||||||
},
|
},
|
||||||
@@ -16909,9 +16909,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rehackt": {
|
"node_modules/rehackt": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.0.6.tgz",
|
||||||
"integrity": "sha512-BI1rV+miEkaHj8zd2n+gaMgzu/fKz7BGlb4zZ6HAiY9adDmJMkaDcmuXlJFv0eyKUob+oszs3/2gdnXUrzx2Tg==",
|
"integrity": "sha512-l3WEzkt4ntlEc/IB3/mF6SRgNHA6zfQR7BlGOgBTOmx7IJJXojDASav+NsgXHFjHn+6RmwqsGPFgZpabWpeOdw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
@@ -19688,9 +19688,9 @@
|
|||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"@apollo/client": {
|
"@apollo/client": {
|
||||||
"version": "3.9.5",
|
"version": "3.9.11",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.9.5.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.9.11.tgz",
|
||||||
"integrity": "sha512-7y+c8MTPU+hhTwvcGVtMMGIgWduzrvG1mz5yJMRyqYbheBkkky3Lki6ADWVSBXG1lZoOtPYvB2zDgVfKb2HSsw==",
|
"integrity": "sha512-H7e9m7cRcFO93tokwzqrsbnfKorkpV24xU30hFH5u2g6B+c1DMo/ouyF/YrBPdrTzqxQCjTUmds/FLmJ7626GA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@graphql-typed-document-node/core": "^3.1.1",
|
"@graphql-typed-document-node/core": "^3.1.1",
|
||||||
@@ -19701,7 +19701,7 @@
|
|||||||
"hoist-non-react-statics": "^3.3.2",
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
"optimism": "^0.18.0",
|
"optimism": "^0.18.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"rehackt": "0.0.5",
|
"rehackt": "0.0.6",
|
||||||
"response-iterator": "^0.2.6",
|
"response-iterator": "^0.2.6",
|
||||||
"symbol-observable": "^4.0.0",
|
"symbol-observable": "^4.0.0",
|
||||||
"ts-invariant": "^0.10.3",
|
"ts-invariant": "^0.10.3",
|
||||||
@@ -22250,9 +22250,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@parse/fs-files-adapter": {
|
"@parse/fs-files-adapter": {
|
||||||
"version": "2.0.1",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-3.0.0.tgz",
|
||||||
"integrity": "sha512-9DY0T9lK73Ysw+wxxsBt9rpxWxJpMlHl/fTW175XSajusW0ZP5jERI3BTKeclV28eVmSU690EO2vnwCURsPZ7g=="
|
"integrity": "sha512-Bb+qLtXQ/1SA2Ck6JLVhfD9JQf6cCwgeDZZJjcIdHzUtdPTFu1hj51xdD7tUCL47Ed2i3aAx6K/M6AjLWYVs3A=="
|
||||||
},
|
},
|
||||||
"@parse/node-apn": {
|
"@parse/node-apn": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
@@ -26154,9 +26154,9 @@
|
|||||||
"integrity": "sha512-q3prnhAL/dBsD+vaGr83B8DzkBijg+Yh+lbt7qp2dW1fpuO+q/upzDXvFJstVsSAA8m11MHGkSxxyxXeLou4MA=="
|
"integrity": "sha512-q3prnhAL/dBsD+vaGr83B8DzkBijg+Yh+lbt7qp2dW1fpuO+q/upzDXvFJstVsSAA8m11MHGkSxxyxXeLou4MA=="
|
||||||
},
|
},
|
||||||
"graphql-relay": {
|
"graphql-relay": {
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.1.tgz",
|
||||||
"integrity": "sha512-44yBuw2/DLNEiMypbNZBt1yMDbBmyVPVesPywnteGGALiBmdyy1JP8jSg8ClLePg8ZZxk0O4BLhd1a6U/1jDOQ==",
|
"integrity": "sha512-8AtwSe6B0/b4+YzynHr38PP7S+zX5Vs5LEo0BEzGCPq/THAiHa5H5ZLf3bRbKbok15ADxDQSsGJmlqXeJDDPIw==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"graphql-tag": {
|
"graphql-tag": {
|
||||||
@@ -32114,9 +32114,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rehackt": {
|
"rehackt": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.0.6.tgz",
|
||||||
"integrity": "sha512-BI1rV+miEkaHj8zd2n+gaMgzu/fKz7BGlb4zZ6HAiY9adDmJMkaDcmuXlJFv0eyKUob+oszs3/2gdnXUrzx2Tg==",
|
"integrity": "sha512-l3WEzkt4ntlEc/IB3/mF6SRgNHA6zfQR7BlGOgBTOmx7IJJXojDASav+NsgXHFjHn+6RmwqsGPFgZpabWpeOdw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "parse-server",
|
"name": "parse-server",
|
||||||
"version": "7.1.0-alpha.4",
|
"version": "7.1.0-alpha.5",
|
||||||
"description": "An express module providing a Parse-compatible API server",
|
"description": "An express module providing a Parse-compatible API server",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"@graphql-tools/merge": "9.0.3",
|
"@graphql-tools/merge": "9.0.3",
|
||||||
"@graphql-tools/schema": "10.0.3",
|
"@graphql-tools/schema": "10.0.3",
|
||||||
"@graphql-tools/utils": "8.12.0",
|
"@graphql-tools/utils": "8.12.0",
|
||||||
"@parse/fs-files-adapter": "2.0.1",
|
"@parse/fs-files-adapter": "3.0.0",
|
||||||
"@parse/push-adapter": "6.0.0",
|
"@parse/push-adapter": "6.0.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"follow-redirects": "1.15.6",
|
"follow-redirects": "1.15.6",
|
||||||
"graphql": "16.8.1",
|
"graphql": "16.8.1",
|
||||||
"graphql-list-fields": "2.0.4",
|
"graphql-list-fields": "2.0.4",
|
||||||
"graphql-relay": "0.10.0",
|
"graphql-relay": "0.10.1",
|
||||||
"graphql-tag": "2.12.6",
|
"graphql-tag": "2.12.6",
|
||||||
"graphql-upload": "15.0.2",
|
"graphql-upload": "15.0.2",
|
||||||
"intersect": "1.0.1",
|
"intersect": "1.0.1",
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/core": "1.10.1",
|
"@actions/core": "1.10.1",
|
||||||
"@apollo/client": "3.9.5",
|
"@apollo/client": "3.9.11",
|
||||||
"@babel/cli": "7.23.9",
|
"@babel/cli": "7.23.9",
|
||||||
"@babel/core": "7.24.3",
|
"@babel/core": "7.24.3",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "7.10.0",
|
"@babel/plugin-proposal-object-rest-spread": "7.10.0",
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
set -x
|
set -x
|
||||||
|
# GITHUB_ACTIONS=true SOURCE_TAG=test ./release_docs.sh
|
||||||
|
|
||||||
if [ "${GITHUB_ACTIONS}" = "" ];
|
if [ "${GITHUB_ACTIONS}" = "" ];
|
||||||
then
|
then
|
||||||
echo "Cannot release docs without GITHUB_ACTIONS set"
|
echo "Cannot release docs without GITHUB_ACTIONS set"
|
||||||
exit 0;
|
exit 0;
|
||||||
fi
|
fi
|
||||||
|
if [ "${SOURCE_TAG}" = "" ];
|
||||||
|
then
|
||||||
|
echo "Cannot release docs without SOURCE_TAG set"
|
||||||
|
exit 0;
|
||||||
|
fi
|
||||||
REPO="https://github.com/parse-community/parse-server"
|
REPO="https://github.com/parse-community/parse-server"
|
||||||
|
|
||||||
rm -rf docs
|
rm -rf docs
|
||||||
@@ -13,20 +20,20 @@ cd docs
|
|||||||
git pull origin gh-pages
|
git pull origin gh-pages
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
DEST="master"
|
RELEASE="release"
|
||||||
|
VERSION="${SOURCE_TAG}"
|
||||||
|
|
||||||
if [ "${SOURCE_TAG}" != "" ];
|
# change the default page to the latest
|
||||||
then
|
echo "<meta http-equiv='refresh' content='0; url=/parse-server/api/${VERSION}'>" > "docs/api/index.html"
|
||||||
DEST="${SOURCE_TAG}"
|
|
||||||
# change the default page to the latest
|
|
||||||
echo "<meta http-equiv='refresh' content='0; url=/parse-server/api/${DEST}'>" > "docs/api/index.html"
|
|
||||||
fi
|
|
||||||
|
|
||||||
npm run definitions
|
npm run definitions
|
||||||
npm run docs
|
npm run docs
|
||||||
|
|
||||||
mkdir -p "docs/api/${DEST}"
|
mkdir -p "docs/api/${RELEASE}"
|
||||||
cp -R out/* "docs/api/${DEST}"
|
cp -R out/* "docs/api/${RELEASE}"
|
||||||
|
|
||||||
|
mkdir -p "docs/api/${VERSION}"
|
||||||
|
cp -R out/* "docs/api/${VERSION}"
|
||||||
|
|
||||||
# Copy other resources
|
# Copy other resources
|
||||||
RESOURCE_DIR=".github"
|
RESOURCE_DIR=".github"
|
||||||
|
|||||||
@@ -254,6 +254,23 @@ function inject(t, list) {
|
|||||||
if (action) {
|
if (action) {
|
||||||
props.push(t.objectProperty(t.stringLiteral('action'), action));
|
props.push(t.objectProperty(t.stringLiteral('action'), action));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t.isGenericTypeAnnotation(elt)) {
|
||||||
|
if (elt.typeAnnotation.id.name in nestedOptionEnvPrefix) {
|
||||||
|
props.push(
|
||||||
|
t.objectProperty(t.stringLiteral('type'), t.stringLiteral(elt.typeAnnotation.id.name))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (t.isArrayTypeAnnotation(elt)) {
|
||||||
|
const elementType = elt.typeAnnotation.elementType;
|
||||||
|
if (t.isGenericTypeAnnotation(elementType)) {
|
||||||
|
if (elementType.id.name in nestedOptionEnvPrefix) {
|
||||||
|
props.push(
|
||||||
|
t.objectProperty(t.stringLiteral('type'), t.stringLiteral(elementType.id.name + '[]'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (elt.defaultValue) {
|
if (elt.defaultValue) {
|
||||||
let parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
|
let parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
|
||||||
if (!parsedValue) {
|
if (!parsedValue) {
|
||||||
|
|||||||
52
spec/ParseConfigKey.spec.js
Normal file
52
spec/ParseConfigKey.spec.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
const Config = require('../lib/Config');
|
||||||
|
const ParseServer = require('../lib/index').ParseServer;
|
||||||
|
|
||||||
|
describe('Config Keys', () => {
|
||||||
|
const tests = [
|
||||||
|
{
|
||||||
|
name: 'Invalid Root Keys',
|
||||||
|
options: { unknow: 'val', masterKeyIPs: '' },
|
||||||
|
error: 'unknow, masterKeyIPs',
|
||||||
|
},
|
||||||
|
{ name: 'Invalid Schema Keys', options: { schema: { Strict: 'val' } }, error: 'schema.Strict' },
|
||||||
|
{
|
||||||
|
name: 'Invalid Pages Keys',
|
||||||
|
options: { pages: { customUrls: { EmailVerificationSendFail: 'val' } } },
|
||||||
|
error: 'pages.customUrls.EmailVerificationSendFail',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Invalid LiveQueryServerOptions Keys',
|
||||||
|
options: { liveQueryServerOptions: { MasterKey: 'value' } },
|
||||||
|
error: 'liveQueryServerOptions.MasterKey',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Invalid RateLimit Keys - Array Item',
|
||||||
|
options: { rateLimit: [{ RequestPath: '' }, { RequestTimeWindow: '' }] },
|
||||||
|
error: 'rateLimit[0].RequestPath, rateLimit[1].RequestTimeWindow',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
tests.forEach(test => {
|
||||||
|
it(test.name, async () => {
|
||||||
|
const logger = require('../lib/logger').logger;
|
||||||
|
spyOn(logger, 'error').and.callThrough();
|
||||||
|
spyOn(Config, 'validateOptions').and.callFake(() => {});
|
||||||
|
|
||||||
|
new ParseServer({
|
||||||
|
...defaultConfiguration,
|
||||||
|
...test.options,
|
||||||
|
});
|
||||||
|
expect(logger.error).toHaveBeenCalledWith(`Invalid Option Keys Found: ${test.error}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should run fine', async () => {
|
||||||
|
try {
|
||||||
|
await reconfigureServer({
|
||||||
|
...defaultConfiguration,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
fail('Should run without error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -64,6 +64,7 @@ export class Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static validateOptions({
|
static validateOptions({
|
||||||
|
customPages,
|
||||||
publicServerURL,
|
publicServerURL,
|
||||||
revokeSessionOnPasswordReset,
|
revokeSessionOnPasswordReset,
|
||||||
expireInactiveSessions,
|
expireInactiveSessions,
|
||||||
@@ -133,9 +134,18 @@ export class Config {
|
|||||||
this.validateRateLimit(rateLimit);
|
this.validateRateLimit(rateLimit);
|
||||||
this.validateLogLevels(logLevels);
|
this.validateLogLevels(logLevels);
|
||||||
this.validateDatabaseOptions(databaseOptions);
|
this.validateDatabaseOptions(databaseOptions);
|
||||||
|
this.validateCustomPages(customPages);
|
||||||
this.validateAllowClientClassCreation(allowClientClassCreation);
|
this.validateAllowClientClassCreation(allowClientClassCreation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static validateCustomPages(customPages) {
|
||||||
|
if (!customPages) return;
|
||||||
|
|
||||||
|
if (Object.prototype.toString.call(customPages) !== '[object Object]') {
|
||||||
|
throw Error('Parse Server option customPages must be an object.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static validateControllers({
|
static validateControllers({
|
||||||
verifyUserEmails,
|
verifyUserEmails,
|
||||||
userController,
|
userController,
|
||||||
@@ -569,6 +579,7 @@ export class Config {
|
|||||||
if (Object.prototype.toString.call(databaseOptions) !== '[object Object]') {
|
if (Object.prototype.toString.call(databaseOptions) !== '[object Object]') {
|
||||||
throw `databaseOptions must be an object`;
|
throw `databaseOptions must be an object`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (databaseOptions.enableSchemaHooks === undefined) {
|
if (databaseOptions.enableSchemaHooks === undefined) {
|
||||||
databaseOptions.enableSchemaHooks = DatabaseOptions.enableSchemaHooks.default;
|
databaseOptions.enableSchemaHooks = DatabaseOptions.enableSchemaHooks.default;
|
||||||
} else if (typeof databaseOptions.enableSchemaHooks !== 'boolean') {
|
} else if (typeof databaseOptions.enableSchemaHooks !== 'boolean') {
|
||||||
|
|||||||
@@ -15,6 +15,4 @@
|
|||||||
*
|
*
|
||||||
* If there are no deprecations, this must return an empty array.
|
* If there are no deprecations, this must return an empty array.
|
||||||
*/
|
*/
|
||||||
module.exports = [
|
module.exports = [{ optionKey: 'encodeParseObjectInCloudFunction', changeNewDefault: 'true' }];
|
||||||
{ optionKey: 'encodeParseObjectInCloudFunction', changeNewDefault: 'true' },
|
|
||||||
];
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ module.exports.ParseServerOptions = {
|
|||||||
env: 'PARSE_SERVER_ACCOUNT_LOCKOUT',
|
env: 'PARSE_SERVER_ACCOUNT_LOCKOUT',
|
||||||
help: 'The account lockout policy for failed login attempts.',
|
help: 'The account lockout policy for failed login attempts.',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'AccountLockoutOptions',
|
||||||
},
|
},
|
||||||
allowClientClassCreation: {
|
allowClientClassCreation: {
|
||||||
env: 'PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION',
|
env: 'PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION',
|
||||||
@@ -157,6 +158,7 @@ module.exports.ParseServerOptions = {
|
|||||||
env: 'PARSE_SERVER_CUSTOM_PAGES',
|
env: 'PARSE_SERVER_CUSTOM_PAGES',
|
||||||
help: 'custom pages for password validation and reset',
|
help: 'custom pages for password validation and reset',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'CustomPagesOptions',
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
databaseAdapter: {
|
databaseAdapter: {
|
||||||
@@ -169,6 +171,7 @@ module.exports.ParseServerOptions = {
|
|||||||
env: 'PARSE_SERVER_DATABASE_OPTIONS',
|
env: 'PARSE_SERVER_DATABASE_OPTIONS',
|
||||||
help: 'Options to pass to the database client',
|
help: 'Options to pass to the database client',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'DatabaseOptions',
|
||||||
},
|
},
|
||||||
databaseURI: {
|
databaseURI: {
|
||||||
env: 'PARSE_SERVER_DATABASE_URI',
|
env: 'PARSE_SERVER_DATABASE_URI',
|
||||||
@@ -273,6 +276,7 @@ module.exports.ParseServerOptions = {
|
|||||||
env: 'PARSE_SERVER_FILE_UPLOAD_OPTIONS',
|
env: 'PARSE_SERVER_FILE_UPLOAD_OPTIONS',
|
||||||
help: 'Options for file uploads',
|
help: 'Options for file uploads',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'FileUploadOptions',
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
graphQLPath: {
|
graphQLPath: {
|
||||||
@@ -294,6 +298,7 @@ module.exports.ParseServerOptions = {
|
|||||||
help:
|
help:
|
||||||
'Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.',
|
'Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'IdempotencyOptions',
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
javascriptKey: {
|
javascriptKey: {
|
||||||
@@ -309,11 +314,13 @@ module.exports.ParseServerOptions = {
|
|||||||
env: 'PARSE_SERVER_LIVE_QUERY',
|
env: 'PARSE_SERVER_LIVE_QUERY',
|
||||||
help: "parse-server's LiveQuery configuration object",
|
help: "parse-server's LiveQuery configuration object",
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'LiveQueryOptions',
|
||||||
},
|
},
|
||||||
liveQueryServerOptions: {
|
liveQueryServerOptions: {
|
||||||
env: 'PARSE_SERVER_LIVE_QUERY_SERVER_OPTIONS',
|
env: 'PARSE_SERVER_LIVE_QUERY_SERVER_OPTIONS',
|
||||||
help: 'Live query server configuration options (will start the liveQuery server)',
|
help: 'Live query server configuration options (will start the liveQuery server)',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'LiveQueryServerOptions',
|
||||||
},
|
},
|
||||||
loggerAdapter: {
|
loggerAdapter: {
|
||||||
env: 'PARSE_SERVER_LOGGER_ADAPTER',
|
env: 'PARSE_SERVER_LOGGER_ADAPTER',
|
||||||
@@ -328,6 +335,7 @@ module.exports.ParseServerOptions = {
|
|||||||
env: 'PARSE_SERVER_LOG_LEVELS',
|
env: 'PARSE_SERVER_LOG_LEVELS',
|
||||||
help: '(Optional) Overrides the log levels used internally by Parse Server to log events.',
|
help: '(Optional) Overrides the log levels used internally by Parse Server to log events.',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'LogLevels',
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
logsFolder: {
|
logsFolder: {
|
||||||
@@ -408,12 +416,14 @@ module.exports.ParseServerOptions = {
|
|||||||
help:
|
help:
|
||||||
'The options for pages such as password reset and email verification. Caution, this is an experimental feature that may not be appropriate for production.',
|
'The options for pages such as password reset and email verification. Caution, this is an experimental feature that may not be appropriate for production.',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'PagesOptions',
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
passwordPolicy: {
|
passwordPolicy: {
|
||||||
env: 'PARSE_SERVER_PASSWORD_POLICY',
|
env: 'PARSE_SERVER_PASSWORD_POLICY',
|
||||||
help: 'The password policy for enforcing password related rules.',
|
help: 'The password policy for enforcing password related rules.',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'PasswordPolicyOptions',
|
||||||
},
|
},
|
||||||
playgroundPath: {
|
playgroundPath: {
|
||||||
env: 'PARSE_SERVER_PLAYGROUND_PATH',
|
env: 'PARSE_SERVER_PLAYGROUND_PATH',
|
||||||
@@ -471,6 +481,7 @@ module.exports.ParseServerOptions = {
|
|||||||
help:
|
help:
|
||||||
"Options to limit repeated requests to Parse Server APIs. This can be used to protect sensitive endpoints such as `/requestPasswordReset` from brute-force attacks or Parse Server as a whole from denial-of-service (DoS) attacks.<br><br>\u2139\uFE0F Mind the following limitations:<br>- rate limits applied per IP address; this limits protection against distributed denial-of-service (DDoS) attacks where many requests are coming from various IP addresses<br>- if multiple Parse Server instances are behind a load balancer or ran in a cluster, each instance will calculate it's own request rates, independent from other instances; this limits the applicability of this feature when using a load balancer and another rate limiting solution that takes requests across all instances into account may be more suitable<br>- this feature provides basic protection against denial-of-service attacks, but a more sophisticated solution works earlier in the request flow and prevents a malicious requests to even reach a server instance; it's therefore recommended to implement a solution according to architecture and user case.",
|
"Options to limit repeated requests to Parse Server APIs. This can be used to protect sensitive endpoints such as `/requestPasswordReset` from brute-force attacks or Parse Server as a whole from denial-of-service (DoS) attacks.<br><br>\u2139\uFE0F Mind the following limitations:<br>- rate limits applied per IP address; this limits protection against distributed denial-of-service (DDoS) attacks where many requests are coming from various IP addresses<br>- if multiple Parse Server instances are behind a load balancer or ran in a cluster, each instance will calculate it's own request rates, independent from other instances; this limits the applicability of this feature when using a load balancer and another rate limiting solution that takes requests across all instances into account may be more suitable<br>- this feature provides basic protection against denial-of-service attacks, but a more sophisticated solution works earlier in the request flow and prevents a malicious requests to even reach a server instance; it's therefore recommended to implement a solution according to architecture and user case.",
|
||||||
action: parsers.arrayParser,
|
action: parsers.arrayParser,
|
||||||
|
type: 'RateLimitOptions[]',
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
readOnlyMasterKey: {
|
readOnlyMasterKey: {
|
||||||
@@ -516,11 +527,13 @@ module.exports.ParseServerOptions = {
|
|||||||
env: 'PARSE_SERVER_SCHEMA',
|
env: 'PARSE_SERVER_SCHEMA',
|
||||||
help: 'Defined schema',
|
help: 'Defined schema',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'SchemaOptions',
|
||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
env: 'PARSE_SERVER_SECURITY',
|
env: 'PARSE_SERVER_SECURITY',
|
||||||
help: 'The security options to identify and report weak security settings.',
|
help: 'The security options to identify and report weak security settings.',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'SecurityOptions',
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
sendUserEmailVerification: {
|
sendUserEmailVerification: {
|
||||||
@@ -665,12 +678,14 @@ module.exports.PagesOptions = {
|
|||||||
env: 'PARSE_SERVER_PAGES_CUSTOM_ROUTES',
|
env: 'PARSE_SERVER_PAGES_CUSTOM_ROUTES',
|
||||||
help: 'The custom routes.',
|
help: 'The custom routes.',
|
||||||
action: parsers.arrayParser,
|
action: parsers.arrayParser,
|
||||||
|
type: 'PagesRoute[]',
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
customUrls: {
|
customUrls: {
|
||||||
env: 'PARSE_SERVER_PAGES_CUSTOM_URLS',
|
env: 'PARSE_SERVER_PAGES_CUSTOM_URLS',
|
||||||
help: 'The URLs to the custom pages.',
|
help: 'The URLs to the custom pages.',
|
||||||
action: parsers.objectParser,
|
action: parsers.objectParser,
|
||||||
|
type: 'PagesCustomUrlsOptions',
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
enableLocalization: {
|
enableLocalization: {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import { SecurityRouter } from './Routers/SecurityRouter';
|
|||||||
import CheckRunner from './Security/CheckRunner';
|
import CheckRunner from './Security/CheckRunner';
|
||||||
import Deprecator from './Deprecator/Deprecator';
|
import Deprecator from './Deprecator/Deprecator';
|
||||||
import { DefinedSchemas } from './SchemaMigrations/DefinedSchemas';
|
import { DefinedSchemas } from './SchemaMigrations/DefinedSchemas';
|
||||||
|
import OptionsDefinitions from './Options/Definitions';
|
||||||
|
|
||||||
// Mutate the Parse object to add the Cloud Code handlers
|
// Mutate the Parse object to add the Cloud Code handlers
|
||||||
addParseCloud();
|
addParseCloud();
|
||||||
@@ -59,6 +60,58 @@ class ParseServer {
|
|||||||
constructor(options: ParseServerOptions) {
|
constructor(options: ParseServerOptions) {
|
||||||
// Scan for deprecated Parse Server options
|
// Scan for deprecated Parse Server options
|
||||||
Deprecator.scanParseServerOptions(options);
|
Deprecator.scanParseServerOptions(options);
|
||||||
|
|
||||||
|
const interfaces = JSON.parse(JSON.stringify(OptionsDefinitions));
|
||||||
|
|
||||||
|
function getValidObject(root) {
|
||||||
|
const result = {};
|
||||||
|
for (const key in root) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(root[key], 'type')) {
|
||||||
|
if (root[key].type.endsWith('[]')) {
|
||||||
|
result[key] = [getValidObject(interfaces[root[key].type.slice(0, -2)])];
|
||||||
|
} else {
|
||||||
|
result[key] = getValidObject(interfaces[root[key].type]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result[key] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionsBlueprint = getValidObject(interfaces['ParseServerOptions']);
|
||||||
|
|
||||||
|
function validateKeyNames(original, ref, name = '') {
|
||||||
|
let result = [];
|
||||||
|
const prefix = name + (name !== '' ? '.' : '');
|
||||||
|
for (const key in original) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(ref, key)) {
|
||||||
|
result.push(prefix + key);
|
||||||
|
} else {
|
||||||
|
if (ref[key] === '') continue;
|
||||||
|
let res = [];
|
||||||
|
if (Array.isArray(original[key]) && Array.isArray(ref[key])) {
|
||||||
|
const type = ref[key][0];
|
||||||
|
original[key].forEach((item, idx) => {
|
||||||
|
if (typeof item === 'object' && item !== null) {
|
||||||
|
res = res.concat(validateKeyNames(item, type, prefix + key + `[${idx}]`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (typeof original[key] === 'object' && typeof ref[key] === 'object') {
|
||||||
|
res = validateKeyNames(original[key], ref[key], prefix + key);
|
||||||
|
}
|
||||||
|
result = result.concat(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const diff = validateKeyNames(options, optionsBlueprint);
|
||||||
|
if (diff.length > 0) {
|
||||||
|
const logger = logging.logger;
|
||||||
|
logger.error(`Invalid Option Keys Found: ${diff.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Set option defaults
|
// Set option defaults
|
||||||
injectDefaults(options);
|
injectDefaults(options);
|
||||||
const {
|
const {
|
||||||
@@ -70,9 +123,9 @@ class ParseServer {
|
|||||||
// Initialize the node client SDK automatically
|
// Initialize the node client SDK automatically
|
||||||
Parse.initialize(appId, javascriptKey || 'unused', masterKey);
|
Parse.initialize(appId, javascriptKey || 'unused', masterKey);
|
||||||
Parse.serverURL = serverURL;
|
Parse.serverURL = serverURL;
|
||||||
|
|
||||||
Config.validateOptions(options);
|
Config.validateOptions(options);
|
||||||
const allControllers = controllers.getControllers(options);
|
const allControllers = controllers.getControllers(options);
|
||||||
|
|
||||||
options.state = 'initialized';
|
options.state = 'initialized';
|
||||||
this.config = Config.put(Object.assign({}, options, allControllers));
|
this.config = Config.put(Object.assign({}, options, allControllers));
|
||||||
this.config.masterKeyIpsStore = new Map();
|
this.config.masterKeyIpsStore = new Map();
|
||||||
|
|||||||
Reference in New Issue
Block a user