build: Release (#9967)

This commit is contained in:
Manuel
2025-12-10 23:41:40 +01:00
committed by GitHub
13 changed files with 855 additions and 218 deletions

View File

@@ -1,6 +1,6 @@
name: ci-performance
on:
pull_request_target:
pull_request:
branches:
- alpha
- beta
@@ -17,8 +17,6 @@ env:
permissions:
contents: read
pull-requests: write
issues: write
jobs:
performance-check:
@@ -172,23 +170,6 @@ jobs:
echo "baseline.json size: $(wc -c < baseline.json) bytes"
echo "pr.json size: $(wc -c < pr.json) bytes"
- name: Store benchmark result (PR)
uses: benchmark-action/github-action-benchmark@v1
if: github.event_name == 'pull_request' && hashFiles('pr.json') != ''
continue-on-error: true
with:
name: Parse Server Performance
tool: 'customSmallerIsBetter'
output-file-path: pr.json
github-token: ${{ secrets.GITHUB_TOKEN }}
auto-push: false
save-data-file: false
alert-threshold: '110%'
comment-on-alert: true
fail-on-alert: false
alert-comment-cc-users: '@parse-community/maintainers'
summary-always: true
- name: Compare benchmark results
id: compare
run: |
@@ -278,43 +259,6 @@ jobs:
path: comparison.md
retention-days: 30
- name: Prepare comment body
if: github.event_name == 'pull_request'
run: |
echo "## Performance Impact Report" > comment.md
echo "" >> comment.md
if [ -f comparison.md ]; then
cat comparison.md >> comment.md
else
echo "⚠️ Could not generate performance comparison." >> comment.md
fi
echo "" >> comment.md
echo "<details>" >> comment.md
echo "<summary>📊 View detailed results</summary>" >> comment.md
echo "" >> comment.md
echo "### Baseline Results" >> comment.md
echo "\`\`\`json" >> comment.md
cat baseline.json >> comment.md
echo "\`\`\`" >> comment.md
echo "" >> comment.md
echo "### PR Results" >> comment.md
echo "\`\`\`json" >> comment.md
cat pr.json >> comment.md
echo "\`\`\`" >> comment.md
echo "" >> comment.md
echo "</details>" >> comment.md
echo "" >> comment.md
echo "> **Note:** Thresholds: ⚠️ >25%, ❌ >50%." >> comment.md
- name: Comment PR with results
if: github.event_name == 'pull_request'
uses: thollander/actions-comment-pull-request@v2
continue-on-error: true
with:
filePath: comment.md
comment_tag: performance-benchmark
mode: recreate
- name: Generate job summary
if: always()
run: |

View File

@@ -1,3 +1,17 @@
# [8.6.0-alpha.2](https://github.com/parse-community/parse-server/compare/8.6.0-alpha.1...8.6.0-alpha.2) (2025-12-10)
### Bug Fixes
* Remove elevated permissions in GitHub CI performance benchmark ([#9966](https://github.com/parse-community/parse-server/issues/9966)) ([6b9f896](https://github.com/parse-community/parse-server/commit/6b9f8963cc3debf59cd9c5dfc5422aff9404ce9d))
# [8.6.0-alpha.1](https://github.com/parse-community/parse-server/compare/8.5.0...8.6.0-alpha.1) (2025-12-03)
### Features
* Add GraphQL query `cloudConfig` to retrieve and mutation `updateCloudConfig` to update Cloud Config ([#9947](https://github.com/parse-community/parse-server/issues/9947)) ([3ca85cd](https://github.com/parse-community/parse-server/commit/3ca85cd4a632f234c9d3d731331c0524dfe54075))
# [8.5.0-alpha.18](https://github.com/parse-community/parse-server/compare/8.5.0-alpha.17...8.5.0-alpha.18) (2025-12-01)

394
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "parse-server",
"version": "8.5.0",
"version": "8.6.0-alpha.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "parse-server",
"version": "8.5.0",
"version": "8.6.0-alpha.2",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -20,7 +20,7 @@
"commander": "13.1.0",
"cors": "2.8.5",
"deepcopy": "2.1.0",
"express": "5.1.0",
"express": "5.2.1",
"express-rate-limit": "7.5.1",
"follow-redirects": "1.15.9",
"graphql": "16.11.0",
@@ -386,38 +386,38 @@
}
},
"node_modules/@apollo/server/node_modules/express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"body-parser": "~1.20.3",
"content-disposition": "~0.5.4",
"content-type": "~1.0.4",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"cookie": "~0.7.1",
"cookie-signature": "~1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"finalhandler": "~1.3.1",
"fresh": "~0.5.2",
"http-errors": "~2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"on-finished": "~2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.12",
"path-to-regexp": "~0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"qs": "~6.14.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.19.0",
"serve-static": "1.16.2",
"send": "~0.19.0",
"serve-static": "~1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"statuses": "~2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
@@ -430,6 +430,20 @@
"url": "https://opencollective.com/express"
}
},
"node_modules/@apollo/server/node_modules/express/node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/@apollo/server/node_modules/finalhandler": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
@@ -7998,23 +8012,42 @@
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"node_modules/body-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
"license": "MIT",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz",
"integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==",
"dependencies": {
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.0",
"debug": "^4.4.3",
"http-errors": "^2.0.0",
"iconv-lite": "^0.6.3",
"iconv-lite": "^0.7.0",
"on-finished": "^2.4.1",
"qs": "^6.14.0",
"raw-body": "^3.0.0",
"type-is": "^2.0.0"
"raw-body": "^3.0.1",
"type-is": "^2.0.1"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/body-parser/node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/body-parser/node_modules/qs": {
@@ -10689,18 +10722,18 @@
}
},
"node_modules/express": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"license": "MIT",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
"body-parser": "^2.2.1",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"depd": "^2.0.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
@@ -12059,23 +12092,21 @@
}
},
"node_modules/google-auth-library/node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"license": "MIT",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/google-auth-library/node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"license": "MIT",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"dependencies": {
"jwa": "^2.0.0",
"jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
@@ -12309,23 +12340,21 @@
}
},
"node_modules/gtoken/node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"license": "MIT",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/gtoken/node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"license": "MIT",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"dependencies": {
"jwa": "^2.0.0",
"jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
@@ -12666,15 +12695,18 @@
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/idb-keyval": {
@@ -13643,11 +13675,11 @@
}
},
"node_modules/jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
"integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
@@ -13670,11 +13702,11 @@
}
},
"node_modules/jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz",
"integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==",
"dependencies": {
"jwa": "^1.4.1",
"jwa": "^1.4.2",
"safe-buffer": "^5.0.1"
}
},
@@ -19581,16 +19613,42 @@
}
},
"node_modules/raw-body": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
"license": "MIT",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.6.3",
"unpipe": "1.0.0"
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.7.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/raw-body/node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"dependencies": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/raw-body/node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"engines": {
"node": ">= 0.8"
}
@@ -22554,21 +22612,21 @@
}
},
"node_modules/web-push/node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/web-push/node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"dependencies": {
"jwa": "^2.0.0",
"jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
@@ -23319,41 +23377,51 @@
}
},
"express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"body-parser": "~1.20.3",
"content-disposition": "~0.5.4",
"content-type": "~1.0.4",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"cookie": "~0.7.1",
"cookie-signature": "~1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"finalhandler": "~1.3.1",
"fresh": "~0.5.2",
"http-errors": "~2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"on-finished": "~2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.12",
"path-to-regexp": "~0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"qs": "~6.14.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.19.0",
"serve-static": "1.16.2",
"send": "~0.19.0",
"serve-static": "~1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"statuses": "~2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"dependencies": {
"qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"requires": {
"side-channel": "^1.1.0"
}
}
}
},
"finalhandler": {
@@ -28606,21 +28674,29 @@
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"body-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz",
"integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==",
"requires": {
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.0",
"debug": "^4.4.3",
"http-errors": "^2.0.0",
"iconv-lite": "^0.6.3",
"iconv-lite": "^0.7.0",
"on-finished": "^2.4.1",
"qs": "^6.14.0",
"raw-body": "^3.0.0",
"type-is": "^2.0.0"
"raw-body": "^3.0.1",
"type-is": "^2.0.1"
},
"dependencies": {
"debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"requires": {
"ms": "^2.1.3"
}
},
"qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
@@ -30496,17 +30572,18 @@
}
},
"express": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"requires": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
"body-parser": "^2.2.1",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"depd": "^2.0.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
@@ -31449,21 +31526,21 @@
}
},
"jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"requires": {
"jwa": "^2.0.0",
"jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
}
@@ -31618,21 +31695,21 @@
},
"dependencies": {
"jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"requires": {
"jwa": "^2.0.0",
"jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
}
@@ -31874,9 +31951,9 @@
"dev": true
},
"iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
@@ -32594,11 +32671,11 @@
}
},
"jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
"integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
@@ -32617,11 +32694,11 @@
}
},
"jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz",
"integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==",
"requires": {
"jwa": "^1.4.1",
"jwa": "^1.4.2",
"safe-buffer": "^5.0.1"
}
},
@@ -36673,14 +36750,33 @@
"requires": {}
},
"raw-body": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"requires": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.6.3",
"unpipe": "1.0.0"
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.7.0",
"unpipe": "~1.0.0"
},
"dependencies": {
"http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"requires": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
}
},
"statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="
}
}
},
"rc": {
@@ -38784,21 +38880,21 @@
}
},
"jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"requires": {
"jwa": "^2.0.0",
"jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "8.5.0",
"version": "8.6.0-alpha.2",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
@@ -30,7 +30,7 @@
"commander": "13.1.0",
"cors": "2.8.5",
"deepcopy": "2.1.0",
"express": "5.1.0",
"express": "5.2.1",
"express-rate-limit": "7.5.1",
"follow-redirects": "1.15.9",
"graphql": "16.11.0",

View File

@@ -155,6 +155,8 @@ function mapperFor(elt, t) {
return wrap(t.identifier('objectParser'));
} else if (t.isBooleanTypeAnnotation(elt)) {
return wrap(t.identifier('booleanParser'));
} else if (t.isObjectTypeAnnotation(elt)) {
return wrap(t.identifier('objectParser'));
} else if (t.isGenericTypeAnnotation(elt)) {
const type = elt.typeAnnotation.id.name;
if (type == 'Adapter') {
@@ -372,12 +374,18 @@ This code has been generated by resources/buildConfigDefinitions.js
Do not edit manually, but update Options/index.js
`;
const babel = require('@babel/core');
const res = babel.transformFileSync('./src/Options/index.js', {
plugins: [plugin, '@babel/transform-flow-strip-types'],
babelrc: false,
auxiliaryCommentBefore,
sourceMaps: false,
});
require('fs').writeFileSync('./src/Options/Definitions.js', res.code + '\n');
require('fs').writeFileSync('./src/Options/docs.js', docs);
// Only run the transformation when executed directly, not when imported by tests
if (require.main === module) {
const babel = require('@babel/core');
const res = babel.transformFileSync('./src/Options/index.js', {
plugins: [plugin, '@babel/transform-flow-strip-types'],
babelrc: false,
auxiliaryCommentBefore,
sourceMaps: false,
});
require('fs').writeFileSync('./src/Options/Definitions.js', res.code + '\n');
require('fs').writeFileSync('./src/Options/docs.js', docs);
}
// Export mapperFor for testing
module.exports = { mapperFor };

View File

@@ -7080,6 +7080,284 @@ describe('ParseGraphQLServer', () => {
});
});
describe("Config Queries", () => {
beforeEach(async () => {
// Setup initial config data
await Parse.Config.save(
{ publicParam: 'publicValue', privateParam: 'privateValue' },
{ privateParam: true },
{ useMasterKey: true }
);
});
it("should return the config value for a specific parameter", async () => {
const query = gql`
query cloudConfig($paramName: String!) {
cloudConfig(paramName: $paramName) {
value
isMasterKeyOnly
}
}
`;
const result = await apolloClient.query({
query,
variables: { paramName: 'publicParam' },
context: {
headers: {
'X-Parse-Master-Key': 'test',
},
},
});
expect(result.errors).toBeUndefined();
expect(result.data.cloudConfig.value).toEqual('publicValue');
expect(result.data.cloudConfig.isMasterKeyOnly).toEqual(false);
});
it("should return null for non-existent parameter", async () => {
const query = gql`
query cloudConfig($paramName: String!) {
cloudConfig(paramName: $paramName) {
value
isMasterKeyOnly
}
}
`;
const result = await apolloClient.query({
query,
variables: { paramName: 'nonExistentParam' },
context: {
headers: {
'X-Parse-Master-Key': 'test',
},
},
});
expect(result.errors).toBeUndefined();
expect(result.data.cloudConfig.value).toBeNull();
expect(result.data.cloudConfig.isMasterKeyOnly).toBeNull();
});
});
describe("Config Mutations", () => {
it("should update a config value using mutation and retrieve it with query", async () => {
const mutation = gql`
mutation updateCloudConfig($input: UpdateCloudConfigInput!) {
updateCloudConfig(input: $input) {
clientMutationId
cloudConfig {
value
isMasterKeyOnly
}
}
}
`;
const query = gql`
query cloudConfig($paramName: String!) {
cloudConfig(paramName: $paramName) {
value
isMasterKeyOnly
}
}
`;
const mutationResult = await apolloClient.mutate({
mutation,
variables: {
input: {
clientMutationId: 'test-mutation-id',
paramName: 'testParam',
value: 'testValue',
isMasterKeyOnly: false,
},
},
context: {
headers: {
'X-Parse-Master-Key': 'test',
},
},
});
expect(mutationResult.errors).toBeUndefined();
expect(mutationResult.data.updateCloudConfig.cloudConfig.value).toEqual('testValue');
expect(mutationResult.data.updateCloudConfig.cloudConfig.isMasterKeyOnly).toEqual(false);
const queryResult = await apolloClient.query({
query,
variables: { paramName: 'testParam' },
context: {
headers: {
'X-Parse-Master-Key': 'test',
},
},
});
expect(queryResult.errors).toBeUndefined();
expect(queryResult.data.cloudConfig.value).toEqual('testValue');
expect(queryResult.data.cloudConfig.isMasterKeyOnly).toEqual(false);
});
it("should update a config value with isMasterKeyOnly set to true", async () => {
const mutation = gql`
mutation updateCloudConfig($input: UpdateCloudConfigInput!) {
updateCloudConfig(input: $input) {
clientMutationId
cloudConfig {
value
isMasterKeyOnly
}
}
}
`;
const query = gql`
query cloudConfig($paramName: String!) {
cloudConfig(paramName: $paramName) {
value
isMasterKeyOnly
}
}
`;
const mutationResult = await apolloClient.mutate({
mutation,
variables: {
input: {
clientMutationId: 'test-mutation-id-2',
paramName: 'privateTestParam',
value: 'privateValue',
isMasterKeyOnly: true,
},
},
context: {
headers: {
'X-Parse-Master-Key': 'test',
},
},
});
expect(mutationResult.errors).toBeUndefined();
expect(mutationResult.data.updateCloudConfig.cloudConfig.value).toEqual('privateValue');
expect(mutationResult.data.updateCloudConfig.cloudConfig.isMasterKeyOnly).toEqual(true);
const queryResult = await apolloClient.query({
query,
variables: { paramName: 'privateTestParam' },
context: {
headers: {
'X-Parse-Master-Key': 'test',
},
},
});
expect(queryResult.errors).toBeUndefined();
expect(queryResult.data.cloudConfig.value).toEqual('privateValue');
expect(queryResult.data.cloudConfig.isMasterKeyOnly).toEqual(true);
});
it("should update an existing config value", async () => {
await Parse.Config.save(
{ existingParam: 'initialValue' },
{},
{ useMasterKey: true }
);
const mutation = gql`
mutation updateCloudConfig($input: UpdateCloudConfigInput!) {
updateCloudConfig(input: $input) {
clientMutationId
cloudConfig {
value
isMasterKeyOnly
}
}
}
`;
const query = gql`
query cloudConfig($paramName: String!) {
cloudConfig(paramName: $paramName) {
value
isMasterKeyOnly
}
}
`;
const mutationResult = await apolloClient.mutate({
mutation,
variables: {
input: {
clientMutationId: 'test-mutation-id-3',
paramName: 'existingParam',
value: 'updatedValue',
isMasterKeyOnly: false,
},
},
context: {
headers: {
'X-Parse-Master-Key': 'test',
},
},
});
expect(mutationResult.errors).toBeUndefined();
expect(mutationResult.data.updateCloudConfig.cloudConfig.value).toEqual('updatedValue');
const queryResult = await apolloClient.query({
query,
variables: { paramName: 'existingParam' },
context: {
headers: {
'X-Parse-Master-Key': 'test',
},
},
});
expect(queryResult.errors).toBeUndefined();
expect(queryResult.data.cloudConfig.value).toEqual('updatedValue');
});
it("should require master key to update config", async () => {
const mutation = gql`
mutation updateCloudConfig($input: UpdateCloudConfigInput!) {
updateCloudConfig(input: $input) {
clientMutationId
cloudConfig {
value
isMasterKeyOnly
}
}
}
`;
try {
await apolloClient.mutate({
mutation,
variables: {
input: {
clientMutationId: 'test-mutation-id-4',
paramName: 'testParam',
value: 'testValue',
isMasterKeyOnly: false,
},
},
context: {
headers: {
'X-Parse-Application-Id': 'test',
},
},
});
fail('Should have thrown an error');
} catch (error) {
expect(error.graphQLErrors).toBeDefined();
expect(error.graphQLErrors[0].message).toContain('Permission denied');
}
});
})
describe('Users Queries', () => {
it('should return current logged user', async () => {
const userName = 'user1',

View File

@@ -0,0 +1,153 @@
const t = require('@babel/types');
const { mapperFor } = require('../resources/buildConfigDefinitions');
describe('buildConfigDefinitions', () => {
describe('mapperFor', () => {
it('should return objectParser for ObjectTypeAnnotation', () => {
const mockElement = {
type: 'ObjectTypeAnnotation',
};
const result = mapperFor(mockElement, t);
expect(t.isMemberExpression(result)).toBe(true);
expect(result.object.name).toBe('parsers');
expect(result.property.name).toBe('objectParser');
});
it('should return objectParser for AnyTypeAnnotation', () => {
const mockElement = {
type: 'AnyTypeAnnotation',
};
const result = mapperFor(mockElement, t);
expect(t.isMemberExpression(result)).toBe(true);
expect(result.object.name).toBe('parsers');
expect(result.property.name).toBe('objectParser');
});
it('should return arrayParser for ArrayTypeAnnotation', () => {
const mockElement = {
type: 'ArrayTypeAnnotation',
};
const result = mapperFor(mockElement, t);
expect(t.isMemberExpression(result)).toBe(true);
expect(result.object.name).toBe('parsers');
expect(result.property.name).toBe('arrayParser');
});
it('should return booleanParser for BooleanTypeAnnotation', () => {
const mockElement = {
type: 'BooleanTypeAnnotation',
};
const result = mapperFor(mockElement, t);
expect(t.isMemberExpression(result)).toBe(true);
expect(result.object.name).toBe('parsers');
expect(result.property.name).toBe('booleanParser');
});
it('should return numberParser call expression for NumberTypeAnnotation', () => {
const mockElement = {
type: 'NumberTypeAnnotation',
name: 'testNumber',
};
const result = mapperFor(mockElement, t);
expect(t.isCallExpression(result)).toBe(true);
expect(result.callee.property.name).toBe('numberParser');
expect(result.arguments[0].value).toBe('testNumber');
});
it('should return moduleOrObjectParser for Adapter GenericTypeAnnotation', () => {
const mockElement = {
type: 'GenericTypeAnnotation',
typeAnnotation: {
id: {
name: 'Adapter',
},
},
};
const result = mapperFor(mockElement, t);
expect(t.isMemberExpression(result)).toBe(true);
expect(result.object.name).toBe('parsers');
expect(result.property.name).toBe('moduleOrObjectParser');
});
it('should return numberOrBooleanParser for NumberOrBoolean GenericTypeAnnotation', () => {
const mockElement = {
type: 'GenericTypeAnnotation',
typeAnnotation: {
id: {
name: 'NumberOrBoolean',
},
},
};
const result = mapperFor(mockElement, t);
expect(t.isMemberExpression(result)).toBe(true);
expect(result.object.name).toBe('parsers');
expect(result.property.name).toBe('numberOrBooleanParser');
});
it('should return numberOrStringParser call expression for NumberOrString GenericTypeAnnotation', () => {
const mockElement = {
type: 'GenericTypeAnnotation',
name: 'testString',
typeAnnotation: {
id: {
name: 'NumberOrString',
},
},
};
const result = mapperFor(mockElement, t);
expect(t.isCallExpression(result)).toBe(true);
expect(result.callee.property.name).toBe('numberOrStringParser');
expect(result.arguments[0].value).toBe('testString');
});
it('should return arrayParser for StringOrStringArray GenericTypeAnnotation', () => {
const mockElement = {
type: 'GenericTypeAnnotation',
typeAnnotation: {
id: {
name: 'StringOrStringArray',
},
},
};
const result = mapperFor(mockElement, t);
expect(t.isMemberExpression(result)).toBe(true);
expect(result.object.name).toBe('parsers');
expect(result.property.name).toBe('arrayParser');
});
it('should return objectParser for unknown GenericTypeAnnotation', () => {
const mockElement = {
type: 'GenericTypeAnnotation',
typeAnnotation: {
id: {
name: 'UnknownType',
},
},
};
const result = mapperFor(mockElement, t);
expect(t.isMemberExpression(result)).toBe(true);
expect(result.object.name).toBe('parsers');
expect(result.property.name).toBe('objectParser');
});
});
});

View File

@@ -49,7 +49,7 @@ const RESERVED_GRAPHQL_TYPE_NAMES = [
'DeleteClassPayload',
'PageInfo',
];
const RESERVED_GRAPHQL_QUERY_NAMES = ['health', 'viewer', 'class', 'classes'];
const RESERVED_GRAPHQL_QUERY_NAMES = ['health', 'viewer', 'class', 'classes', 'cloudConfig'];
const RESERVED_GRAPHQL_MUTATION_NAMES = [
'signUp',
'logIn',
@@ -59,6 +59,7 @@ const RESERVED_GRAPHQL_MUTATION_NAMES = [
'createClass',
'updateClass',
'deleteClass',
'updateCloudConfig',
];
class ParseGraphQLSchema {
@@ -118,6 +119,7 @@ class ParseGraphQLSchema {
this.functionNamesString = functionNamesString;
this.parseClassTypes = {};
this.viewerType = null;
this.cloudConfigType = null;
this.graphQLAutoSchema = null;
this.graphQLSchema = null;
this.graphQLTypes = [];

View File

@@ -0,0 +1,76 @@
import { GraphQLNonNull, GraphQLString, GraphQLBoolean } from 'graphql';
import { mutationWithClientMutationId } from 'graphql-relay';
import Parse from 'parse/node';
import { createSanitizedError } from '../../Error';
import GlobalConfigRouter from '../../Routers/GlobalConfigRouter';
const globalConfigRouter = new GlobalConfigRouter();
const updateCloudConfig = async (context, paramName, value, isMasterKeyOnly = false) => {
const { config, auth } = context;
if (!auth.isMaster) {
throw createSanitizedError(
Parse.Error.OPERATION_FORBIDDEN,
'Master Key is required to update GlobalConfig.'
);
}
await globalConfigRouter.updateGlobalConfig({
body: {
params: { [paramName]: value },
masterKeyOnly: { [paramName]: isMasterKeyOnly },
},
config,
auth,
context,
});
return { value, isMasterKeyOnly };
};
const load = parseGraphQLSchema => {
const updateCloudConfigMutation = mutationWithClientMutationId({
name: 'UpdateCloudConfig',
description: 'Updates the value of a specific parameter in GlobalConfig.',
inputFields: {
paramName: {
description: 'The name of the parameter to set.',
type: new GraphQLNonNull(GraphQLString),
},
value: {
description: 'The value to set for the parameter.',
type: new GraphQLNonNull(GraphQLString),
},
isMasterKeyOnly: {
description: 'Whether this parameter should only be accessible with master key.',
type: GraphQLBoolean,
defaultValue: false,
},
},
outputFields: {
cloudConfig: {
description: 'The updated config value.',
type: new GraphQLNonNull(parseGraphQLSchema.cloudConfigType),
},
},
mutateAndGetPayload: async (args, context) => {
try {
const { paramName, value, isMasterKeyOnly } = args;
const result = await updateCloudConfig(context, paramName, value, isMasterKeyOnly);
return {
cloudConfig: result,
};
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
});
parseGraphQLSchema.addGraphQLType(updateCloudConfigMutation.args.input.type.ofType, true, true);
parseGraphQLSchema.addGraphQLType(updateCloudConfigMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation('updateCloudConfig', updateCloudConfigMutation, true, true);
};
export { load, updateCloudConfig };

View File

@@ -0,0 +1,61 @@
import { GraphQLNonNull, GraphQLString, GraphQLBoolean, GraphQLObjectType } from 'graphql';
import Parse from 'parse/node';
import { createSanitizedError } from '../../Error';
const cloudConfig = async (context, paramName) => {
const { config, auth } = context;
if (!auth.isMaster) {
throw createSanitizedError(
Parse.Error.OPERATION_FORBIDDEN,
'Master Key is required to access GlobalConfig.'
);
}
const results = await config.database.find('_GlobalConfig', { objectId: '1' }, { limit: 1 });
if (results.length !== 1) {
return { value: null, isMasterKeyOnly: null };
}
const globalConfig = results[0];
const params = globalConfig.params || {};
const masterKeyOnly = globalConfig.masterKeyOnly || {};
if (params[paramName] !== undefined) {
return { value: params[paramName], isMasterKeyOnly: masterKeyOnly[paramName] ?? null };
}
return { value: null, isMasterKeyOnly: null };
};
const load = (parseGraphQLSchema) => {
if (!parseGraphQLSchema.cloudConfigType) {
const cloudConfigType = new GraphQLObjectType({
name: 'ConfigValue',
fields: {
value: { type: GraphQLString },
isMasterKeyOnly: { type: GraphQLBoolean },
},
});
parseGraphQLSchema.addGraphQLType(cloudConfigType, true, true);
parseGraphQLSchema.cloudConfigType = cloudConfigType;
}
parseGraphQLSchema.addGraphQLQuery('cloudConfig', {
description: 'Returns the value of a specific parameter from GlobalConfig.',
args: {
paramName: { type: new GraphQLNonNull(GraphQLString) },
},
type: new GraphQLNonNull(parseGraphQLSchema.cloudConfigType),
async resolve(_source, args, context) {
try {
return await cloudConfig(context, args.paramName);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
}, false, true);
};
export { load, cloudConfig };

View File

@@ -2,12 +2,14 @@ import * as filesMutations from './filesMutations';
import * as usersMutations from './usersMutations';
import * as functionsMutations from './functionsMutations';
import * as schemaMutations from './schemaMutations';
import * as configMutations from './configMutations';
const load = parseGraphQLSchema => {
filesMutations.load(parseGraphQLSchema);
usersMutations.load(parseGraphQLSchema);
functionsMutations.load(parseGraphQLSchema);
schemaMutations.load(parseGraphQLSchema);
configMutations.load(parseGraphQLSchema);
};
export { load };

View File

@@ -1,6 +1,7 @@
import { GraphQLNonNull, GraphQLBoolean } from 'graphql';
import * as usersQueries from './usersQueries';
import * as schemaQueries from './schemaQueries';
import * as configQueries from './configQueries';
const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLQuery(
@@ -16,6 +17,7 @@ const load = parseGraphQLSchema => {
usersQueries.load(parseGraphQLSchema);
schemaQueries.load(parseGraphQLSchema);
configQueries.load(parseGraphQLSchema);
};
export { load };

View File

@@ -111,6 +111,7 @@ module.exports.ParseServerOptions = {
env: 'PARSE_SERVER_AUTH_PROVIDERS',
help:
'Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication',
action: parsers.objectParser,
},
cacheAdapter: {
env: 'PARSE_SERVER_CACHE_ADAPTER',