From f6427acfdfe4dd7b01385779604edda9f29f122a Mon Sep 17 00:00:00 2001 From: Antoine Cormouls Date: Thu, 27 Jan 2022 18:53:16 +0100 Subject: [PATCH 01/14] feat: improve performance by using indexed parseClasses --- src/GraphQL/ParseGraphQLSchema.js | 26 ++++++++-------------- src/GraphQL/helpers/objectsQueries.js | 20 +++-------------- src/GraphQL/loaders/defaultGraphQLTypes.js | 4 ++-- src/GraphQL/transformers/mutation.js | 2 +- src/GraphQL/transformers/query.js | 2 +- 5 files changed, 16 insertions(+), 38 deletions(-) diff --git a/src/GraphQL/ParseGraphQLSchema.js b/src/GraphQL/ParseGraphQLSchema.js index 011905ca2d..7b942463f8 100644 --- a/src/GraphQL/ParseGraphQLSchema.js +++ b/src/GraphQL/ParseGraphQLSchema.js @@ -93,10 +93,14 @@ class ParseGraphQLSchema { async load() { const { parseGraphQLConfig } = await this._initializeSchemaAndConfig(); - const parseClasses = await this._getClassesForSchema(parseGraphQLConfig); + const parseClassesArray = await this._getClassesForSchema(parseGraphQLConfig); const functionNames = await this._getFunctionNames(); const functionNamesString = JSON.stringify(functionNames); + const parseClasses = parseClassesArray.reduce((acc, clazz) => { + acc[clazz.className] = clazz; + return acc; + }, {}); if ( !this._hasSchemaInputChanged({ parseClasses, @@ -127,7 +131,7 @@ class ParseGraphQLSchema { defaultRelaySchema.load(this); schemaTypes.load(this); - this._getParseClassesWithConfig(parseClasses, parseGraphQLConfig).forEach( + this._getParseClassesWithConfig(parseClassesArray, parseGraphQLConfig).forEach( ([parseClass, parseClassConfig]) => { // Some times schema return the _auth_data_ field // it will lead to unstable graphql generation order @@ -155,7 +159,7 @@ class ParseGraphQLSchema { } ); - defaultGraphQLTypes.loadArrayResult(this, parseClasses); + defaultGraphQLTypes.loadArrayResult(this, parseClassesArray); defaultGraphQLQueries.load(this); defaultGraphQLMutations.load(this); @@ -500,29 +504,17 @@ class ParseGraphQLSchema { const { parseClasses, parseGraphQLConfig, functionNamesString } = params; // First init - if (!this.parseCachedClasses || !this.graphQLSchema) { - const thisParseClassesObj = parseClasses.reduce((acc, clzz) => { - acc[clzz.className] = clzz; - return acc; - }, {}); - this.parseCachedClasses = thisParseClassesObj; + if (!this.graphQLSchema) { return true; } - const newParseCachedClasses = parseClasses.reduce((acc, clzz) => { - acc[clzz.className] = clzz; - return acc; - }, {}); - if ( isDeepStrictEqual(this.parseGraphQLConfig, parseGraphQLConfig) && this.functionNamesString === functionNamesString && - isDeepStrictEqual(this.parseCachedClasses, newParseCachedClasses) + isDeepStrictEqual(this.parseClasses, parseClasses) ) { return false; } - - this.parseCachedClasses = newParseCachedClasses; return true; } } diff --git a/src/GraphQL/helpers/objectsQueries.js b/src/GraphQL/helpers/objectsQueries.js index 7b973312ce..ce0b05a2c7 100644 --- a/src/GraphQL/helpers/objectsQueries.js +++ b/src/GraphQL/helpers/objectsQueries.js @@ -12,9 +12,7 @@ const needToGetAllKeys = (fields, keys, parseClasses) => if (fields[key[0]]) { if (fields[key[0]].type === 'Relation') return false; if (fields[key[0]].type === 'Pointer') { - const subClass = parseClasses.find( - ({ className: parseClassName }) => fields[key[0]].targetClass === parseClassName - ); + const subClass = parseClasses[fields[key[0]].targetClass]; if (subClass && subClass.fields[key[1]]) { // Current sub key is not custom return false; @@ -48,13 +46,7 @@ const getObject = async ( ) => { const options = {}; try { - if ( - !needToGetAllKeys( - parseClasses.find(({ className: parseClassName }) => className === parseClassName).fields, - keys, - parseClasses - ) - ) { + if (!needToGetAllKeys(parseClasses[className].fields, keys, parseClasses)) { options.keys = keys; } } catch (e) { @@ -165,13 +157,7 @@ const findObjects = async ( // Silently replace the limit on the query with the max configured options.limit = config.maxLimit; } - if ( - !needToGetAllKeys( - parseClasses.find(({ className: parseClassName }) => className === parseClassName).fields, - keys, - parseClasses - ) - ) { + if (!needToGetAllKeys(parseClasses[className].fields, keys, parseClasses)) { options.keys = keys; } if (includeAll === true) { diff --git a/src/GraphQL/loaders/defaultGraphQLTypes.js b/src/GraphQL/loaders/defaultGraphQLTypes.js index f340cfb35f..1520f0c53b 100644 --- a/src/GraphQL/loaders/defaultGraphQLTypes.js +++ b/src/GraphQL/loaders/defaultGraphQLTypes.js @@ -1190,8 +1190,8 @@ const ELEMENT = new GraphQLObjectType({ // Default static union type, we update types and resolveType function later let ARRAY_RESULT; -const loadArrayResult = (parseGraphQLSchema, parseClasses) => { - const classTypes = parseClasses +const loadArrayResult = (parseGraphQLSchema, parseClassesArray) => { + const classTypes = parseClassesArray .filter(parseClass => parseGraphQLSchema.parseClassTypes[parseClass.className].classGraphQLOutputType ? true : false ) diff --git a/src/GraphQL/transformers/mutation.js b/src/GraphQL/transformers/mutation.js index 2a21816fe9..5b72d6f05d 100644 --- a/src/GraphQL/transformers/mutation.js +++ b/src/GraphQL/transformers/mutation.js @@ -14,7 +14,7 @@ const transformTypes = async ( classGraphQLUpdateType, config: { isCreateEnabled, isUpdateEnabled }, } = parseGraphQLSchema.parseClassTypes[className]; - const parseClass = parseGraphQLSchema.parseClasses.find(clazz => clazz.className === className); + const parseClass = parseGraphQLSchema.parseClasses[className]; if (fields) { const classGraphQLCreateTypeFields = isCreateEnabled && classGraphQLCreateType ? classGraphQLCreateType.getFields() : null; diff --git a/src/GraphQL/transformers/query.js b/src/GraphQL/transformers/query.js index 92ea7be5fe..a91ee208ad 100644 --- a/src/GraphQL/transformers/query.js +++ b/src/GraphQL/transformers/query.js @@ -51,7 +51,7 @@ const transformQueryConstraintInputToParse = ( parentConstraints, parseClasses ) => { - const fields = parseClasses.find(parseClass => parseClass.className === className).fields; + const fields = parseClasses[className].fields; if (parentFieldName === 'id' && className) { Object.keys(constraints).forEach(constraintName => { const constraintValue = constraints[constraintName]; From 4d5d935d830a7823b0395dc054fdcedb67fd6c2e Mon Sep 17 00:00:00 2001 From: Antoine Cormouls Date: Fri, 28 Jan 2022 09:25:56 +0100 Subject: [PATCH 02/14] fix: schema test --- spec/ParseGraphQLSchema.spec.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/ParseGraphQLSchema.spec.js b/spec/ParseGraphQLSchema.spec.js index a06a230f6a..0d4cd7d721 100644 --- a/spec/ParseGraphQLSchema.spec.js +++ b/spec/ParseGraphQLSchema.spec.js @@ -57,7 +57,6 @@ describe('ParseGraphQLSchema', () => { it('should load a brand new GraphQL Schema if Parse Schema changes', async () => { await parseGraphQLSchema.load(); const parseClasses = parseGraphQLSchema.parseClasses; - const parseCachedClasses = parseGraphQLSchema.parseCachedClasses; const parseClassTypes = parseGraphQLSchema.parseClassTypes; const graphQLSchema = parseGraphQLSchema.graphQLSchema; const graphQLTypes = parseGraphQLSchema.graphQLTypes; @@ -70,7 +69,6 @@ describe('ParseGraphQLSchema', () => { await new Promise(resolve => setTimeout(resolve, 200)); await parseGraphQLSchema.load(); expect(parseClasses).not.toBe(parseGraphQLSchema.parseClasses); - expect(parseCachedClasses).not.toBe(parseGraphQLSchema.parseCachedClasses); expect(parseClassTypes).not.toBe(parseGraphQLSchema.parseClassTypes); expect(graphQLSchema).not.toBe(parseGraphQLSchema.graphQLSchema); expect(graphQLTypes).not.toBe(parseGraphQLSchema.graphQLTypes); @@ -94,7 +92,6 @@ describe('ParseGraphQLSchema', () => { }); await parseGraphQLSchema.load(); const parseClasses = parseGraphQLSchema.parseClasses; - const parseCachedClasses = parseGraphQLSchema.parseCachedClasses; const parseClassTypes = parseGraphQLSchema.parseClassTypes; const graphQLSchema = parseGraphQLSchema.graphQLSchema; const graphQLTypes = parseGraphQLSchema.graphQLTypes; @@ -109,7 +106,6 @@ describe('ParseGraphQLSchema', () => { await new Promise(resolve => setTimeout(resolve, 200)); await parseGraphQLSchema.load(); expect(parseClasses).not.toBe(parseGraphQLSchema.parseClasses); - expect(parseCachedClasses).not.toBe(parseGraphQLSchema.parseCachedClasses); expect(parseClassTypes).not.toBe(parseGraphQLSchema.parseClassTypes); expect(graphQLSchema).not.toBe(parseGraphQLSchema.graphQLSchema); expect(graphQLTypes).not.toBe(parseGraphQLSchema.graphQLTypes); From 9ef19687ba0f26ee4a8c566d9d0049bb9e61736e Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Fri, 25 Mar 2022 19:47:05 +0100 Subject: [PATCH 03/14] ci: fix node engine check (#7891) --- ci/nodeEngineCheck.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ci/nodeEngineCheck.js b/ci/nodeEngineCheck.js index da68f314b1..a68f2c593c 100644 --- a/ci/nodeEngineCheck.js +++ b/ci/nodeEngineCheck.js @@ -75,17 +75,21 @@ class NodeEngineCheck { // For each file for (const file of files) { - // Get node version const contentString = await fs.readFile(file, 'utf-8'); - const contentJson = JSON.parse(contentString); - const version = ((contentJson || {}).engines || {}).node; - - // Add response - response.push({ - file: file, - nodeVersion: version - }); + try { + const contentJson = JSON.parse(contentString); + const version = ((contentJson || {}).engines || {}).node; + + // Add response + response.push({ + file: file, + nodeVersion: version + }); + } catch(e) { + console.log(`Ignoring file because it is not valid JSON: ${file}`); + core.warning(`Ignoring file because it is not valid JSON: ${file}`); + } } // If results should be cleaned by removing undefined node versions From f63fb2b338c908f0e7a648d338c26b9daa50c8f2 Mon Sep 17 00:00:00 2001 From: dblythy Date: Sat, 26 Mar 2022 13:39:16 +1100 Subject: [PATCH 04/14] fix: return correct response when revert is used in beforeSave (#7839) --- spec/CloudCode.spec.js | 134 +++++++++++++++++++++++++++++++++++++++++ src/RestWrite.js | 59 +++++++++--------- 2 files changed, 165 insertions(+), 28 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 4b8df9f9c9..faaa6b826a 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -1494,6 +1494,110 @@ describe('Cloud Code', () => { }); }); + it('before save can revert fields', async () => { + Parse.Cloud.beforeSave('TestObject', ({ object }) => { + object.revert('foo'); + return object; + }); + + Parse.Cloud.afterSave('TestObject', ({ object }) => { + expect(object.get('foo')).toBeUndefined(); + return object; + }); + + const obj = new TestObject(); + obj.set('foo', 'bar'); + await obj.save(); + + expect(obj.get('foo')).toBeUndefined(); + await obj.fetch(); + + expect(obj.get('foo')).toBeUndefined(); + }); + + it('before save can revert fields with existing object', async () => { + Parse.Cloud.beforeSave( + 'TestObject', + ({ object }) => { + object.revert('foo'); + return object; + }, + { + skipWithMasterKey: true, + } + ); + + Parse.Cloud.afterSave( + 'TestObject', + ({ object }) => { + expect(object.get('foo')).toBe('bar'); + return object; + }, + { + skipWithMasterKey: true, + } + ); + + const obj = new TestObject(); + obj.set('foo', 'bar'); + await obj.save(null, { useMasterKey: true }); + + expect(obj.get('foo')).toBe('bar'); + obj.set('foo', 'yolo'); + await obj.save(); + expect(obj.get('foo')).toBe('bar'); + }); + + it('can unset in afterSave', async () => { + Parse.Cloud.beforeSave('TestObject', ({ object }) => { + if (!object.existed()) { + object.set('secret', true); + return object; + } + object.revert('secret'); + }); + + Parse.Cloud.afterSave('TestObject', ({ object }) => { + object.unset('secret'); + }); + + Parse.Cloud.beforeFind( + 'TestObject', + ({ query }) => { + query.exclude('secret'); + }, + { + skipWithMasterKey: true, + } + ); + + const obj = new TestObject(); + await obj.save(); + expect(obj.get('secret')).toBeUndefined(); + await obj.fetch(); + expect(obj.get('secret')).toBeUndefined(); + await obj.fetch({ useMasterKey: true }); + expect(obj.get('secret')).toBe(true); + }); + + it('should revert in beforeSave', async () => { + Parse.Cloud.beforeSave('MyObject', ({ object }) => { + if (!object.existed()) { + object.set('count', 0); + return object; + } + object.revert('count'); + return object; + }); + const obj = await new Parse.Object('MyObject').save(); + expect(obj.get('count')).toBe(0); + obj.set('count', 10); + await obj.save(); + expect(obj.get('count')).toBe(0); + await obj.fetch(); + expect(obj.get('count')).toBe(0); + }); + it('beforeSave should not sanitize database', async done => { const { adapter } = Config.get(Parse.applicationId).database; const spy = spyOn(adapter, 'findOneAndUpdate').and.callThrough(); @@ -1860,6 +1964,36 @@ describe('afterSave hooks', () => { const myObject = new MyObject(); myObject.save().then(() => done()); }); + + it('should unset in afterSave', async () => { + Parse.Cloud.afterSave( + 'MyObject', + ({ object }) => { + object.unset('secret'); + }, + { + skipWithMasterKey: true, + } + ); + const obj = new Parse.Object('MyObject'); + obj.set('secret', 'bar'); + await obj.save(); + expect(obj.get('secret')).toBeUndefined(); + await obj.fetch(); + expect(obj.get('secret')).toBe('bar'); + }); + + it('should unset', async () => { + Parse.Cloud.beforeSave('MyObject', ({ object }) => { + object.set('secret', 'hidden'); + }); + + Parse.Cloud.afterSave('MyObject', ({ object }) => { + object.unset('secret'); + }); + const obj = await new Parse.Object('MyObject').save(); + expect(obj.get('secret')).toBeUndefined(); + }); }); describe('beforeDelete hooks', () => { diff --git a/src/RestWrite.js b/src/RestWrite.js index 8b728731da..3e20328a9a 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -95,6 +95,7 @@ function RestWrite(config, auth, className, query, data, originalData, clientSDK // Shared SchemaController to be reused to reduce the number of loadSchema() calls per request // Once set the schemaData should be immutable this.validSchemaController = null; + this.pendingOps = {}; } // A convenient method to perform all the steps of processing the @@ -225,18 +226,11 @@ RestWrite.prototype.runBeforeSaveTrigger = function () { return Promise.resolve(); } - // Cloud code gets a bit of extra data for its objects - var extraData = { className: this.className }; - if (this.query && this.query.objectId) { - extraData.objectId = this.query.objectId; - } + const { originalObject, updatedObject } = this.buildParseObjects(); - let originalObject = null; - const updatedObject = this.buildUpdatedObject(extraData); - if (this.query && this.query.objectId) { - // This is an update for existing object. - originalObject = triggers.inflate(extraData, this.originalData); - } + const stateController = Parse.CoreManager.getObjectStateController(); + const [pending] = stateController.getPendingOps(updatedObject._getStateIdentifier()); + this.pendingOps = { ...pending }; return Promise.resolve() .then(() => { @@ -1531,20 +1525,7 @@ RestWrite.prototype.runAfterSaveTrigger = function () { return Promise.resolve(); } - var extraData = { className: this.className }; - if (this.query && this.query.objectId) { - extraData.objectId = this.query.objectId; - } - - // Build the original object, we only do this for a update write. - let originalObject; - if (this.query && this.query.objectId) { - originalObject = triggers.inflate(extraData, this.originalData); - } - - // Build the inflated object, different from beforeSave, originalData is not empty - // since developers can change data in the beforeSave. - const updatedObject = this.buildUpdatedObject(extraData); + const { originalObject, updatedObject } = this.buildParseObjects(); updatedObject._handleSaveResponse(this.response.response, this.response.status || 200); this.config.database.loadSchema().then(schemaController => { @@ -1569,8 +1550,15 @@ RestWrite.prototype.runAfterSaveTrigger = function () { this.context ) .then(result => { - if (result && typeof result === 'object') { + const jsonReturned = result && !result._toFullJSON; + if (jsonReturned) { + this.pendingOps = {}; this.response.response = result; + } else { + this.response.response = this._updateResponseWithData( + (result || updatedObject)._toFullJSON(), + this.data + ); } }) .catch(function (err) { @@ -1604,7 +1592,13 @@ RestWrite.prototype.sanitizedData = function () { }; // Returns an updated copy of the object -RestWrite.prototype.buildUpdatedObject = function (extraData) { +RestWrite.prototype.buildParseObjects = function () { + const extraData = { className: this.className, objectId: this.query?.objectId }; + let originalObject; + if (this.query && this.query.objectId) { + originalObject = triggers.inflate(extraData, this.originalData); + } + const className = Parse.Object.fromJSON(extraData); const readOnlyAttributes = className.constructor.readOnlyAttributes ? className.constructor.readOnlyAttributes() @@ -1642,7 +1636,7 @@ RestWrite.prototype.buildUpdatedObject = function (extraData) { delete sanitized[attribute]; } updatedObject.set(sanitized); - return updatedObject; + return { updatedObject, originalObject }; }; RestWrite.prototype.cleanUserAuthData = function () { @@ -1662,6 +1656,15 @@ RestWrite.prototype.cleanUserAuthData = function () { }; RestWrite.prototype._updateResponseWithData = function (response, data) { + const { updatedObject } = this.buildParseObjects(); + const stateController = Parse.CoreManager.getObjectStateController(); + const [pending] = stateController.getPendingOps(updatedObject._getStateIdentifier()); + for (const key in this.pendingOps) { + if (!pending[key]) { + data[key] = this.originalData ? this.originalData[key] : { __op: 'Delete' }; + this.storage.fieldsChangedByTrigger.push(key); + } + } if (_.isEmpty(this.storage.fieldsChangedByTrigger)) { return response; } From b3199d739775dd1ecb2e000b1734f71120e18e3d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 26 Mar 2022 02:40:21 +0000 Subject: [PATCH 05/14] chore(release): 5.2.1-alpha.1 [skip ci] ## [5.2.1-alpha.1](https://github.com/parse-community/parse-server/compare/5.2.0...5.2.1-alpha.1) (2022-03-26) ### Bug Fixes * return correct response when revert is used in beforeSave ([#7839](https://github.com/parse-community/parse-server/issues/7839)) ([f63fb2b](https://github.com/parse-community/parse-server/commit/f63fb2b338c908f0e7a648d338c26b9daa50c8f2)) --- changelogs/CHANGELOG_alpha.md | 7 +++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/changelogs/CHANGELOG_alpha.md b/changelogs/CHANGELOG_alpha.md index 6938ec3c74..511c994d0e 100644 --- a/changelogs/CHANGELOG_alpha.md +++ b/changelogs/CHANGELOG_alpha.md @@ -1,3 +1,10 @@ +## [5.2.1-alpha.1](https://github.com/parse-community/parse-server/compare/5.2.0...5.2.1-alpha.1) (2022-03-26) + + +### Bug Fixes + +* return correct response when revert is used in beforeSave ([#7839](https://github.com/parse-community/parse-server/issues/7839)) ([f63fb2b](https://github.com/parse-community/parse-server/commit/f63fb2b338c908f0e7a648d338c26b9daa50c8f2)) + # [5.2.0-alpha.3](https://github.com/parse-community/parse-server/compare/5.2.0-alpha.2...5.2.0-alpha.3) (2022-03-24) diff --git a/package-lock.json b/package-lock.json index 32800119bb..5468d2c4f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.2.0", + "version": "5.2.1-alpha.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2bc25fba02..a5ab014590 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.2.0", + "version": "5.2.1-alpha.1", "description": "An express module providing a Parse-compatible API server", "main": "lib/index.js", "repository": { From 48bd512eeb47666967dff8c5e723ddc5b7801daa Mon Sep 17 00:00:00 2001 From: dblythy Date: Sun, 27 Mar 2022 01:29:39 +1100 Subject: [PATCH 06/14] perf: reduce database operations when using the constant parameter in Cloud Function validation (#7892) --- src/triggers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/triggers.js b/src/triggers.js index 8320b5fb74..360166d0aa 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -738,7 +738,7 @@ async function builtInTriggerValidator(options, request, auth) { } if (opt.constant && request.object) { if (request.original) { - request.object.set(key, request.original.get(key)); + request.object.revert(key); } else if (opt.default != null) { request.object.set(key, opt.default); } From e0cca580e7105b4a9a6ac0c73b02c8e7fa54839c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 26 Mar 2022 14:30:48 +0000 Subject: [PATCH 07/14] chore(release): 5.2.1-alpha.2 [skip ci] ## [5.2.1-alpha.2](https://github.com/parse-community/parse-server/compare/5.2.1-alpha.1...5.2.1-alpha.2) (2022-03-26) ### Performance Improvements * reduce database operations when using the constant parameter in Cloud Function validation ([#7892](https://github.com/parse-community/parse-server/issues/7892)) ([48bd512](https://github.com/parse-community/parse-server/commit/48bd512eeb47666967dff8c5e723ddc5b7801daa)) --- changelogs/CHANGELOG_alpha.md | 7 +++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/changelogs/CHANGELOG_alpha.md b/changelogs/CHANGELOG_alpha.md index 511c994d0e..dcbe2dd4ed 100644 --- a/changelogs/CHANGELOG_alpha.md +++ b/changelogs/CHANGELOG_alpha.md @@ -1,3 +1,10 @@ +## [5.2.1-alpha.2](https://github.com/parse-community/parse-server/compare/5.2.1-alpha.1...5.2.1-alpha.2) (2022-03-26) + + +### Performance Improvements + +* reduce database operations when using the constant parameter in Cloud Function validation ([#7892](https://github.com/parse-community/parse-server/issues/7892)) ([48bd512](https://github.com/parse-community/parse-server/commit/48bd512eeb47666967dff8c5e723ddc5b7801daa)) + ## [5.2.1-alpha.1](https://github.com/parse-community/parse-server/compare/5.2.0...5.2.1-alpha.1) (2022-03-26) diff --git a/package-lock.json b/package-lock.json index 5468d2c4f4..d37e18d2dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.2.1-alpha.1", + "version": "5.2.1-alpha.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a5ab014590..a2395fafe0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.2.1-alpha.1", + "version": "5.2.1-alpha.2", "description": "An express module providing a Parse-compatible API server", "main": "lib/index.js", "repository": { From 90155cf1680e5e0499b0000e071c6cb0ce3aef96 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 27 Mar 2022 03:59:16 +0200 Subject: [PATCH 08/14] feat: add MongoDB 5.1 compatibility (#7682) --- .github/workflows/ci.yml | 5 ++ README.md | 23 ++--- package.json | 9 +- spec/MongoStorageAdapter.spec.js | 36 +++++++- spec/ParseQuery.hint.spec.js | 144 +++++++++++++++++++++++++++++-- 5 files changed, 195 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2463901a91..f8584ff9cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,6 +101,11 @@ jobs: strategy: matrix: include: + - name: MongoDB 5.1, ReplicaSet, WiredTiger + MONGODB_VERSION: 5.1.0 + MONGODB_TOPOLOGY: replicaset + MONGODB_STORAGE_ENGINE: wiredTiger + NODE_VERSION: 14.18.1 - name: MongoDB 5.0, ReplicaSet, WiredTiger MONGODB_VERSION: 5.0.3 MONGODB_TOPOLOGY: replicaset diff --git a/README.md b/README.md index 7b507e9c80..5a5d88c3d6 100644 --- a/README.md +++ b/README.md @@ -112,8 +112,8 @@ Before you start make sure you have installed: #### Node.js Parse Server is continuously tested with the most recent releases of Node.js to ensure compatibility. We follow the [Node.js Long Term Support plan](https://github.com/nodejs/Release) and only test against versions that are officially supported and have not reached their end-of-life date. -| Version | Latest Version | End-of-Life | Compatible | -|------------|----------------|-------------|---------------| +| Version | Latest Version | End-of-Life | Compatible | +|------------|----------------|-------------|--------------| | Node.js 12 | 12.22.7 | April 2022 | ✅ Yes | | Node.js 14 | 14.18.1 | April 2023 | ✅ Yes | | Node.js 16 | 16.13.0 | April 2024 | ✅ Yes | @@ -124,20 +124,21 @@ Parse Server is continuously tested with the most recent releases of MongoDB to | Version | Latest Version | End-of-Life | Compatible | |-------------|----------------|--------------|------------| -| MongoDB 4.0 | 4.0.27 | April 2022 | ✅ Yes | -| MongoDB 4.2 | 4.2.17 | TBD | ✅ Yes | -| MongoDB 4.4 | 4.4.10 | TBD | ✅ Yes | -| MongoDB 5.0 | 5.0.3 | January 2024 | ✅ Yes | - +| MongoDB 4.0 | 4.0.27 | April 2022 | ✅ Yes | +| MongoDB 4.2 | 4.2.17 | TBD | ✅ Yes | +| MongoDB 4.4 | 4.4.10 | TBD | ✅ Yes | +| MongoDB 5.0 | 5.0.3 | January 2024 | ✅ Yes | +| MongoDB 5.1 | 5.1.0 | January 2024 | ✅ Yes | + #### PostgreSQL Parse Server is continuously tested with the most recent releases of PostgreSQL and PostGIS to ensure compatibility, using [PostGIS docker images](https://registry.hub.docker.com/r/postgis/postgis/tags?page=1&ordering=last_updated). We follow the [PostgreSQL support schedule](https://www.postgresql.org/support/versioning) and [PostGIS support schedule](https://www.postgis.net/eol_policy/) and only test against versions that are officially supported and have not reached their end-of-life date. Due to the extensive PostgreSQL support duration of 5 years, Parse Server drops support if a version is older than 3.5 years and a newer version has been available for at least 2.5 years. | Version | PostGIS Version | End-of-Life | Parse Server Support End | Compatible | |-------------|-----------------|---------------|--------------------------|------------| -| Postgres 11 | 3.0, 3.1, 3.2 | November 2023 | April 2022 | ✅ Yes | -| Postgres 12 | 3.2 | November 2024 | April 2023 | ✅ Yes | -| Postgres 13 | 3.2 | November 2025 | April 2024 | ✅ Yes | -| Postgres 14 | 3.2 | November 2026 | April 2025 | ✅ Yes | +| Postgres 11 | 3.0, 3.1, 3.2 | November 2023 | April 2022 | ✅ Yes | +| Postgres 12 | 3.2 | November 2024 | April 2023 | ✅ Yes | +| Postgres 13 | 3.2 | November 2025 | April 2024 | ✅ Yes | +| Postgres 14 | 3.2 | November 2026 | April 2025 | ✅ Yes | ### Locally ```bash diff --git a/package.json b/package.json index a2395fafe0..d8d229ab38 100644 --- a/package.json +++ b/package.json @@ -120,12 +120,13 @@ "test:mongodb:4.2.17": "npm run test:mongodb --dbversion=4.2.17", "test:mongodb:4.4.10": "npm run test:mongodb --dbversion=4.4.10", "test:mongodb:5.0.5": "npm run test:mongodb --dbversion=5.0.5", + "test:mongodb:5.1.0": "npm run test:mongodb --dbversion=5.1.0", "posttest:mongodb": "mongodb-runner stop", - "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", - "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", + "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", + "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", "test": "npm run testonly", - "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop", - "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine", + "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop", + "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine", "start": "node ./bin/parse-server", "prettier": "prettier --write {src,spec}/{**/*,*}.js", "prepare": "npm run build", diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index a31a6134f7..130971a535 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -308,7 +308,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined); }); - it('should use index for caseInsensitive query', async () => { + it_only_mongodb_version('<5.1')('should use index for caseInsensitive query', async () => { const user = new Parse.User(); user.set('username', 'Bugs'); user.set('password', 'Bunny'); @@ -342,6 +342,40 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH'); }); + it_only_mongodb_version('>=5.1')('should use index for caseInsensitive query', async () => { + const user = new Parse.User(); + user.set('username', 'Bugs'); + user.set('password', 'Bunny'); + await user.signUp(); + + const database = Config.get(Parse.applicationId).database; + await database.adapter.dropAllIndexes('_User'); + + const preIndexPlan = await database.find( + '_User', + { username: 'bugs' }, + { caseInsensitive: true, explain: true } + ); + + const schema = await new Parse.Schema('_User').get(); + + await database.adapter.ensureIndex( + '_User', + schema, + ['username'], + 'case_insensitive_username', + true + ); + + const postIndexPlan = await database.find( + '_User', + { username: 'bugs' }, + { caseInsensitive: true, explain: true } + ); + expect(preIndexPlan.queryPlanner.winningPlan.queryPlan.stage).toBe('COLLSCAN'); + expect(postIndexPlan.queryPlanner.winningPlan.queryPlan.stage).toBe('FETCH'); + }); + it('should delete field without index', async () => { const database = Config.get(Parse.applicationId).database; const obj = new Parse.Object('MyObject'); diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index 2685137801..8ceb441d5b 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -27,7 +27,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { await TestUtils.destroyAllDataPermanently(false); }); - it('query find with hint string', async () => { + it_only_mongodb_version('<5.1')('query find with hint string', async () => { const object = new TestObject(); await object.save(); @@ -39,7 +39,18 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); - it('query find with hint object', async () => { + it_only_mongodb_version('>=5.1')('query find with hint string', async () => { + const object = new TestObject(); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + const explain = await collection._rawFind({ _id: object.id }, { hint: '_id_', explain: true }); + expect(explain.queryPlanner.winningPlan.queryPlan.stage).toBe('FETCH'); + expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('IXSCAN'); + expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.indexName).toBe('_id_'); + }); + + it_only_mongodb_version('<5.1')('query find with hint object', async () => { const object = new TestObject(); await object.save(); @@ -53,6 +64,20 @@ describe_only_db('mongo')('Parse.Query hint', () => { }); }); + it_only_mongodb_version('>=5.1')('query find with hint object', async () => { + const object = new TestObject(); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + const explain = await collection._rawFind( + { _id: object.id }, + { hint: { _id: 1 }, explain: true } + ); + expect(explain.queryPlanner.winningPlan.queryPlan.stage).toBe('FETCH'); + expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('IXSCAN'); + expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.keyPattern).toEqual({ _id: 1 }); + }); + it_only_mongodb_version('<4.4')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -73,7 +98,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=4.4')('query aggregate with hint string', async () => { + it_only_mongodb_version('>=4.4<5.1')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -97,6 +122,30 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); + it_only_mongodb_version('>=5.1')('query aggregate with hint string', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + explain: true, + }); + let { queryPlanner } = result[0].stages[0].$cursor; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: '_id_', + explain: true, + }); + queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + }); + it_only_mongodb_version('<4.4')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -117,7 +166,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=4.4')('query aggregate with hint object', async () => { + it_only_mongodb_version('>=4.4<5.1')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -142,7 +191,32 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it('query find with hint (rest)', async () => { + it_only_mongodb_version('>=5.1')('query aggregate with hint object', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + explain: true, + }); + let { queryPlanner } = result[0].stages[0].$cursor; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: { _id: 1 }, + explain: true, + }); + queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); + }); + + it_only_mongodb_version('<5.1')('query find with hint (rest)', async () => { const object = new TestObject(); await object.save(); let options = Object.assign({}, masterKeyOptions, { @@ -167,6 +241,31 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); + it_only_mongodb_version('>=5.1')('query find with hint (rest)', async () => { + const object = new TestObject(); + await object.save(); + let options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/classes/TestObject', + qs: { + explain: true, + }, + }); + let response = await request(options); + let explain = response.data.results; + expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + + options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/classes/TestObject', + qs: { + explain: true, + hint: '_id_', + }, + }); + response = await request(options); + explain = response.data.results; + expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + }); + it_only_mongodb_version('<4.4')('query aggregate with hint (rest)', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -194,7 +293,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=4.4')('query aggregate with hint (rest)', async () => { + it_only_mongodb_version('>=4.4<5.1')('query aggregate with hint (rest)', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); let options = Object.assign({}, masterKeyOptions, { @@ -226,4 +325,37 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); + + it_only_mongodb_version('>=5.1')('query aggregate with hint (rest)', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + let options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/aggregate/TestObject', + qs: { + explain: true, + group: JSON.stringify({ objectId: '$foo' }), + }, + }); + let response = await request(options); + let { queryPlanner } = response.data.results[0].stages[0].$cursor; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + + options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/aggregate/TestObject', + qs: { + explain: true, + hint: '_id_', + group: JSON.stringify({ objectId: '$foo' }), + }, + }); + response = await request(options); + queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); + }); }); From 499cead1de6751c88442a7da4a24096017b231cb Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 27 Mar 2022 02:00:17 +0000 Subject: [PATCH 09/14] chore(release): 5.3.0-alpha.1 [skip ci] # [5.3.0-alpha.1](https://github.com/parse-community/parse-server/compare/5.2.1-alpha.2...5.3.0-alpha.1) (2022-03-27) ### Features * add MongoDB 5.1 compatibility ([#7682](https://github.com/parse-community/parse-server/issues/7682)) ([90155cf](https://github.com/parse-community/parse-server/commit/90155cf1680e5e0499b0000e071c6cb0ce3aef96)) --- changelogs/CHANGELOG_alpha.md | 7 +++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/changelogs/CHANGELOG_alpha.md b/changelogs/CHANGELOG_alpha.md index dcbe2dd4ed..a61aef3404 100644 --- a/changelogs/CHANGELOG_alpha.md +++ b/changelogs/CHANGELOG_alpha.md @@ -1,3 +1,10 @@ +# [5.3.0-alpha.1](https://github.com/parse-community/parse-server/compare/5.2.1-alpha.2...5.3.0-alpha.1) (2022-03-27) + + +### Features + +* add MongoDB 5.1 compatibility ([#7682](https://github.com/parse-community/parse-server/issues/7682)) ([90155cf](https://github.com/parse-community/parse-server/commit/90155cf1680e5e0499b0000e071c6cb0ce3aef96)) + ## [5.2.1-alpha.2](https://github.com/parse-community/parse-server/compare/5.2.1-alpha.1...5.2.1-alpha.2) (2022-03-26) diff --git a/package-lock.json b/package-lock.json index d37e18d2dd..dacb8759d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.2.1-alpha.2", + "version": "5.3.0-alpha.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d8d229ab38..35ac12f220 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.2.1-alpha.2", + "version": "5.3.0-alpha.1", "description": "An express module providing a Parse-compatible API server", "main": "lib/index.js", "repository": { From ef56e98ef65041b4d3b7b82cce3473269c27f6fd Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Sun, 27 Mar 2022 15:17:48 +0200 Subject: [PATCH 10/14] fix: security upgrade parse push adapter from 4.1.0 to 4.1.2 (#7893) --- package-lock.json | 84 +++++++++++++++++++++++++---------------------- package.json | 4 +-- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index dacb8759d8..47bdc1593d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1109,11 +1109,11 @@ } }, "@babel/runtime-corejs3": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.6.tgz", - "integrity": "sha512-Xl8SPYtdjcMoCsIM4teyVRg7jIcgl8F2kRtoCcXuHzXswt9UxZCS6BzRo8fcnCuP6u2XtPgvyonmEPF57Kxo9Q==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz", + "integrity": "sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA==", "requires": { - "core-js-pure": "^3.14.0", + "core-js-pure": "^3.15.0", "regenerator-runtime": "^0.13.4" } }, @@ -1850,20 +1850,20 @@ "dev": true }, "@parse/node-apn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-5.1.0.tgz", - "integrity": "sha512-WT3iVwr1Y/Jf4nq4RGNwBdLwm3gTodsb+g3IY98MPSJ7LCNf+R81Nj/nQO5r/twJfN1v5B8cAgfvPGs2rPelvg==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-5.1.3.tgz", + "integrity": "sha512-Bwhmbm895lEIF2772PJ8dSvBjrtOG9/q/TDMxmX40IgZxQFoXS73+JUIKTq3CA7SUB/Szu5roJINQ0L2U/1MJw==", "requires": { - "debug": "4.3.2", + "debug": "4.3.3", "jsonwebtoken": "8.5.1", - "node-forge": "0.10.0", - "verror": "1.10.0" + "node-forge": "1.3.0", + "verror": "1.10.1" }, "dependencies": { "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { "ms": "2.1.2" } @@ -1872,6 +1872,16 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } } } }, @@ -1886,42 +1896,36 @@ } }, "@parse/push-adapter": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-4.1.0.tgz", - "integrity": "sha512-8SOU4zgIr3+wn6Hbge4X/zAYAcJR7puJ3aY2ri+8fqMARgBria4JkIeAyKaTG/mUMHw6Qy5DpYYRe0LjImjZNw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-4.1.2.tgz", + "integrity": "sha512-034vZTlAzgdfefIY4+Q4j8DHS/VwUAIVoh1JeRkHNfyQmUQ++uKbQbUQdJ/nf11HHS69kwLENs13BmhlHMpyHQ==", "requires": { - "@parse/node-apn": "5.1.0", + "@parse/node-apn": "5.1.3", "@parse/node-gcm": "1.0.2", "npmlog": "4.1.2", - "parse": "3.3.0" + "parse": "3.4.0" }, "dependencies": { "@babel/runtime": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", - "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", + "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", "requires": { "regenerator-runtime": "^0.13.4" } }, - "crypto-js": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz", - "integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==", - "optional": true - }, "parse": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/parse/-/parse-3.3.0.tgz", - "integrity": "sha512-SQkTDupU7JQBJpYFIpO8TlQjUtjboUdkXaak57pjoC1ZVbhaiNyLsdYbrlM0B+sNYhlvcMh7zwZW48u10+zm0A==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/parse/-/parse-3.4.0.tgz", + "integrity": "sha512-FMZLxPW6PvrBgxkXc9AmnYsFKvPwiS4G2n9OI4mdfiSoNzIVLc+bXzlUdJ+I7hiqHsBTP0BrdQczw2/cnVkJ6w==", "requires": { - "@babel/runtime": "7.14.6", - "@babel/runtime-corejs3": "7.14.6", - "crypto-js": "4.0.0", + "@babel/runtime": "7.15.4", + "@babel/runtime-corejs3": "7.14.7", + "crypto-js": "4.1.1", "idb-keyval": "5.0.6", "react-native-crypto-js": "1.0.0", "uuid": "3.4.0", - "ws": "7.5.0", + "ws": "7.5.1", "xmlhttprequest": "1.8.0" } }, @@ -1931,9 +1935,9 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "ws": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", - "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==" + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", + "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==" } } }, @@ -10899,9 +10903,9 @@ } }, "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", + "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==" }, "node-netstat": { "version": "1.8.0", diff --git a/package.json b/package.json index 35ac12f220..147ac79f56 100644 --- a/package.json +++ b/package.json @@ -19,13 +19,13 @@ ], "license": "BSD-3-Clause", "dependencies": { + "@apollo/client": "3.5.8", "@apollographql/graphql-playground-html": "1.6.29", "@graphql-tools/links": "8.2.2", - "@apollo/client": "3.5.8", "@graphql-tools/stitch": "6.2.4", "@graphql-tools/utils": "6.2.4", "@parse/fs-files-adapter": "1.2.1", - "@parse/push-adapter": "4.1.0", + "@parse/push-adapter": "4.1.2", "apollo-server-express": "2.25.2", "bcryptjs": "2.4.3", "body-parser": "1.19.1", From 119dbe0cd5c68b435736948553eba68e974a3334 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 27 Mar 2022 13:18:57 +0000 Subject: [PATCH 11/14] chore(release): 5.3.0-alpha.2 [skip ci] # [5.3.0-alpha.2](https://github.com/parse-community/parse-server/compare/5.3.0-alpha.1...5.3.0-alpha.2) (2022-03-27) ### Bug Fixes * security upgrade parse push adapter from 4.1.0 to 4.1.2 ([#7893](https://github.com/parse-community/parse-server/issues/7893)) ([ef56e98](https://github.com/parse-community/parse-server/commit/ef56e98ef65041b4d3b7b82cce3473269c27f6fd)) --- changelogs/CHANGELOG_alpha.md | 7 +++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/changelogs/CHANGELOG_alpha.md b/changelogs/CHANGELOG_alpha.md index a61aef3404..c3e5136322 100644 --- a/changelogs/CHANGELOG_alpha.md +++ b/changelogs/CHANGELOG_alpha.md @@ -1,3 +1,10 @@ +# [5.3.0-alpha.2](https://github.com/parse-community/parse-server/compare/5.3.0-alpha.1...5.3.0-alpha.2) (2022-03-27) + + +### Bug Fixes + +* security upgrade parse push adapter from 4.1.0 to 4.1.2 ([#7893](https://github.com/parse-community/parse-server/issues/7893)) ([ef56e98](https://github.com/parse-community/parse-server/commit/ef56e98ef65041b4d3b7b82cce3473269c27f6fd)) + # [5.3.0-alpha.1](https://github.com/parse-community/parse-server/compare/5.2.1-alpha.2...5.3.0-alpha.1) (2022-03-27) diff --git a/package-lock.json b/package-lock.json index 47bdc1593d..f1ebbfdc0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.3.0-alpha.1", + "version": "5.3.0-alpha.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 147ac79f56..a89abb4821 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.3.0-alpha.1", + "version": "5.3.0-alpha.2", "description": "An express module providing a Parse-compatible API server", "main": "lib/index.js", "repository": { From 6b4b358f0842ae920e45652f5e8b2afebc6caf3a Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Sun, 27 Mar 2022 22:44:02 +0200 Subject: [PATCH 12/14] feat: add MongoDB 5.2 support (#7894) --- .github/workflows/ci.yml | 9 +++- README.md | 15 +++--- package.json | 13 +++--- spec/ParseQuery.hint.spec.js | 88 ++++++++++++++++++++++++++++++++++-- 4 files changed, 107 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8584ff9cb..924139b944 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,13 +101,18 @@ jobs: strategy: matrix: include: + - name: MongoDB 5.2, ReplicaSet, WiredTiger + MONGODB_VERSION: 5.2.1 + MONGODB_TOPOLOGY: replicaset + MONGODB_STORAGE_ENGINE: wiredTiger + NODE_VERSION: 14.18.1 - name: MongoDB 5.1, ReplicaSet, WiredTiger - MONGODB_VERSION: 5.1.0 + MONGODB_VERSION: 5.1.1 MONGODB_TOPOLOGY: replicaset MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 14.18.1 - name: MongoDB 5.0, ReplicaSet, WiredTiger - MONGODB_VERSION: 5.0.3 + MONGODB_VERSION: 5.0.6 MONGODB_TOPOLOGY: replicaset MONGODB_STORAGE_ENGINE: wiredTiger NODE_VERSION: 16.13.0 diff --git a/README.md b/README.md index 5a5d88c3d6..dc14dd7f1a 100644 --- a/README.md +++ b/README.md @@ -122,13 +122,14 @@ Parse Server is continuously tested with the most recent releases of Node.js to #### MongoDB Parse Server is continuously tested with the most recent releases of MongoDB to ensure compatibility. We follow the [MongoDB support schedule](https://www.mongodb.com/support-policy) and only test against versions that are officially supported and have not reached their end-of-life date. -| Version | Latest Version | End-of-Life | Compatible | -|-------------|----------------|--------------|------------| -| MongoDB 4.0 | 4.0.27 | April 2022 | ✅ Yes | -| MongoDB 4.2 | 4.2.17 | TBD | ✅ Yes | -| MongoDB 4.4 | 4.4.10 | TBD | ✅ Yes | -| MongoDB 5.0 | 5.0.3 | January 2024 | ✅ Yes | -| MongoDB 5.1 | 5.1.0 | January 2024 | ✅ Yes | +| Version | Latest Version | End-of-Life | Compatible | +|-------------|----------------|-------------|------------| +| MongoDB 4.0 | 4.0.27 | April 2022 | ✅ Yes | +| MongoDB 4.2 | 4.2.17 | TBD | ✅ Yes | +| MongoDB 4.4 | 4.4.10 | TBD | ✅ Yes | +| MongoDB 5.0 | 5.0.6 | TBD | ✅ Yes | +| MongoDB 5.1 | 5.1.1 | TBD | ✅ Yes | +| MongoDB 5.2 | 5.2.1 | TBD | ✅ Yes | #### PostgreSQL Parse Server is continuously tested with the most recent releases of PostgreSQL and PostGIS to ensure compatibility, using [PostGIS docker images](https://registry.hub.docker.com/r/postgis/postgis/tags?page=1&ordering=last_updated). We follow the [PostgreSQL support schedule](https://www.postgresql.org/support/versioning) and [PostGIS support schedule](https://www.postgis.net/eol_policy/) and only test against versions that are officially supported and have not reached their end-of-life date. Due to the extensive PostgreSQL support duration of 5 years, Parse Server drops support if a version is older than 3.5 years and a newer version has been available for at least 2.5 years. diff --git a/package.json b/package.json index a89abb4821..01cac39632 100644 --- a/package.json +++ b/package.json @@ -119,14 +119,15 @@ "test:mongodb:4.0.27": "npm run test:mongodb --dbversion=4.0.27", "test:mongodb:4.2.17": "npm run test:mongodb --dbversion=4.2.17", "test:mongodb:4.4.10": "npm run test:mongodb --dbversion=4.4.10", - "test:mongodb:5.0.5": "npm run test:mongodb --dbversion=5.0.5", - "test:mongodb:5.1.0": "npm run test:mongodb --dbversion=5.1.0", + "test:mongodb:5.0.6": "npm run test:mongodb --dbversion=5.0.6", + "test:mongodb:5.1.1": "npm run test:mongodb --dbversion=5.1.1", + "test:mongodb:5.2.1": "npm run test:mongodb --dbversion=5.2.1", "posttest:mongodb": "mongodb-runner stop", - "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", - "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", + "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.2.1} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", + "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.2.1} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", "test": "npm run testonly", - "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop", - "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine", + "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.2.1} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop", + "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.2.1} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine", "start": "node ./bin/parse-server", "prettier": "prettier --write {src,spec}/{**/*,*}.js", "prepare": "npm run build", diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index 8ceb441d5b..db45106359 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -122,7 +122,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=5.1')('query aggregate with hint string', async () => { + it_only_mongodb_version('>=5.1<5.2')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -146,6 +146,30 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); }); + it_only_mongodb_version('>=5.2')('query aggregate with hint string', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + explain: true, + }); + let queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: '_id_', + explain: true, + }); + queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + }); + it_only_mongodb_version('<4.4')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -191,7 +215,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=5.1')('query aggregate with hint object', async () => { + it_only_mongodb_version('>=5.1<5.2')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -216,6 +240,31 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); + it_only_mongodb_version('>=5.2')('query aggregate with hint object', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + explain: true, + }); + let queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: { _id: 1 }, + explain: true, + }); + queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); + }); + it_only_mongodb_version('<5.1')('query find with hint (rest)', async () => { const object = new TestObject(); await object.save(); @@ -326,7 +375,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=5.1')('query aggregate with hint (rest)', async () => { + it_only_mongodb_version('>=5.1<5.2')('query aggregate with hint (rest)', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); let options = Object.assign({}, masterKeyOptions, { @@ -358,4 +407,37 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); + + it_only_mongodb_version('>=5.2')('query aggregate with hint (rest)', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + let options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/aggregate/TestObject', + qs: { + explain: true, + group: JSON.stringify({ objectId: '$foo' }), + }, + }); + let response = await request(options); + let queryPlanner = response.data.results[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + + options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/aggregate/TestObject', + qs: { + explain: true, + hint: '_id_', + group: JSON.stringify({ objectId: '$foo' }), + }, + }); + response = await request(options); + queryPlanner = response.data.results[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); + }); }); From 75eca2d52fee63b21f4ce0e75a3043f533c046bb Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 27 Mar 2022 20:46:31 +0000 Subject: [PATCH 13/14] chore(release): 5.3.0-alpha.3 [skip ci] # [5.3.0-alpha.3](https://github.com/parse-community/parse-server/compare/5.3.0-alpha.2...5.3.0-alpha.3) (2022-03-27) ### Features * add MongoDB 5.2 support ([#7894](https://github.com/parse-community/parse-server/issues/7894)) ([6b4b358](https://github.com/parse-community/parse-server/commit/6b4b358f0842ae920e45652f5e8b2afebc6caf3a)) --- changelogs/CHANGELOG_alpha.md | 7 +++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/changelogs/CHANGELOG_alpha.md b/changelogs/CHANGELOG_alpha.md index c3e5136322..888add258c 100644 --- a/changelogs/CHANGELOG_alpha.md +++ b/changelogs/CHANGELOG_alpha.md @@ -1,3 +1,10 @@ +# [5.3.0-alpha.3](https://github.com/parse-community/parse-server/compare/5.3.0-alpha.2...5.3.0-alpha.3) (2022-03-27) + + +### Features + +* add MongoDB 5.2 support ([#7894](https://github.com/parse-community/parse-server/issues/7894)) ([6b4b358](https://github.com/parse-community/parse-server/commit/6b4b358f0842ae920e45652f5e8b2afebc6caf3a)) + # [5.3.0-alpha.2](https://github.com/parse-community/parse-server/compare/5.3.0-alpha.1...5.3.0-alpha.2) (2022-03-27) diff --git a/package-lock.json b/package-lock.json index f1ebbfdc0a..46e7dab718 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.3.0-alpha.2", + "version": "5.3.0-alpha.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 01cac39632..5cd91c0978 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "5.3.0-alpha.2", + "version": "5.3.0-alpha.3", "description": "An express module providing a Parse-compatible API server", "main": "lib/index.js", "repository": { From 132a3e081304dc35bc1155d109f40cea00e76e37 Mon Sep 17 00:00:00 2001 From: Snyk bot Date: Sun, 3 Apr 2022 15:09:04 +0100 Subject: [PATCH 14/14] refactor: upgrade mongodb from 4.3.1 to 4.4.1 (#7906) --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 46e7dab718..8a0335dede 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10447,21 +10447,21 @@ "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "mongodb": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.3.1.tgz", - "integrity": "sha512-sNa8APSIk+r4x31ZwctKjuPSaeKuvUeNb/fu/3B6dRM02HpEgig7hTHM8A/PJQTlxuC/KFWlDlQjhsk/S43tBg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.4.1.tgz", + "integrity": "sha512-IAD3nFtCR4s22vi5qjqkCBnuyDDrOW8WVSSmgHquOvGaP1iTD+XpC5tr8wAUbZ2EeZkaswwBKQFHDvl4qYcKqQ==", "requires": { "bson": "^4.6.1", "denque": "^2.0.1", - "mongodb-connection-string-url": "^2.4.1", + "mongodb-connection-string-url": "^2.5.2", "saslprep": "^1.0.3", - "socks": "^2.6.1" + "socks": "^2.6.2" }, "dependencies": { "bson": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz", - "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.2.tgz", + "integrity": "sha512-VeJKHShcu1b/ugl0QiujlVuBepab714X9nNyBdA1kfekuDGecxgpTA2Z6nYbagrWFeiIyzSWIOzju3lhj+RNyQ==", "requires": { "buffer": "^5.6.0" } @@ -10474,9 +10474,9 @@ } }, "mongodb-connection-string-url": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.4.1.tgz", - "integrity": "sha512-d5Kd2bVsKcSA7YI/yo57fSTtMwRQdFkvc5IZwod1RRxJtECeWPPSo7zqcUGJELifRA//Igs4spVtYAmvFCatug==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.2.tgz", + "integrity": "sha512-tWDyIG8cQlI5k3skB6ywaEA5F9f5OntrKKsT/Lteub2zgwSUlhqEN2inGgBTm8bpYJf8QYBdA/5naz65XDpczA==", "requires": { "@types/whatwg-url": "^8.2.1", "whatwg-url": "^11.0.0" @@ -15536,12 +15536,12 @@ } }, "socks": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", - "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", "requires": { "ip": "^1.1.5", - "smart-buffer": "^4.1.0" + "smart-buffer": "^4.2.0" } }, "sort-keys": { diff --git a/package.json b/package.json index 5cd91c0978..7aa9f07f4a 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "lodash": "4.17.21", "lru-cache": "6.0.0", "mime": "3.0.0", - "mongodb": "4.3.1", + "mongodb": "4.4.1", "mustache": "4.2.0", "parse": "3.4.1", "pg-monitor": "1.4.1",