diff --git a/package-lock.json b/package-lock.json index 17701ad364..045195ff54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2650,6 +2650,20 @@ "@types/node": "*" } }, + "@types/webidl-conversions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", + "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" + }, + "@types/whatwg-url": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz", + "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==", + "requires": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, "@types/ws": { "version": "7.4.7", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", @@ -3603,8 +3617,7 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "bcrypt-nodejs": { "version": "0.0.3", @@ -3652,6 +3665,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dev": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -3788,13 +3802,13 @@ "bson": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", - "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "dev": true }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -7940,8 +7954,7 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { "version": "4.0.6", @@ -8153,6 +8166,11 @@ "loose-envify": "^1.0.0" } }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -10388,16 +10406,63 @@ "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "mongodb": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.11.tgz", - "integrity": "sha512-4Y4lTFHDHZZdgMaHmojtNAlqkvddX2QQBEN0K//GzxhGwlI9tZ9R0vhbjr1Decw+TF7qK0ZLjQT292XgHRRQgw==", - "requires": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "optional-require": "^1.0.3", - "safe-buffer": "^5.1.2", - "saslprep": "^1.0.0" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.3.1.tgz", + "integrity": "sha512-sNa8APSIk+r4x31ZwctKjuPSaeKuvUeNb/fu/3B6dRM02HpEgig7hTHM8A/PJQTlxuC/KFWlDlQjhsk/S43tBg==", + "requires": { + "bson": "^4.6.1", + "denque": "^2.0.1", + "mongodb-connection-string-url": "^2.4.1", + "saslprep": "^1.0.3", + "socks": "^2.6.1" + }, + "dependencies": { + "bson": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz", + "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==", + "requires": { + "buffer": "^5.6.0" + } + }, + "denque": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==" + } + } + }, + "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==", + "requires": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + }, + "dependencies": { + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "requires": { + "punycode": "^2.1.1" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + } } }, "mongodb-core": { @@ -10520,6 +10585,20 @@ "ms": "2.1.2" } }, + "mongodb": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz", + "integrity": "sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==", + "dev": true, + "requires": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "optional-require": "^1.1.8", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -13233,9 +13312,10 @@ } }, "optional-require": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.7.tgz", - "integrity": "sha512-cIeRZocXsZnZYn+SevbtSqNlLbeoS4mLzuNn4fvXRMDRNhTGg0sxuKXl0FnZCtnew85LorNxIbZp5OeliILhMw==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", + "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", + "dev": true, "requires": { "require-at": "^1.0.6" } @@ -14518,7 +14598,8 @@ "require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", - "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==" + "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", + "dev": true }, "require-directory": { "version": "2.1.1", @@ -15277,6 +15358,11 @@ } } }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -15411,6 +15497,15 @@ } } }, + "socks": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", + "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, "sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", diff --git a/package.json b/package.json index 5067906b30..8129cb2f19 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "lodash": "4.17.21", "lru-cache": "5.1.1", "mime": "3.0.0", - "mongodb": "3.6.11", + "mongodb": "4.3.1", "mustache": "4.2.0", "parse": "3.4.1", "pg-monitor": "1.4.1", @@ -118,12 +118,13 @@ "test:mongodb:4.0.27": "npm run test:mongodb --dbversion=4.0.27", "test:mongodb:4.2.17": "npm run test:mongodb --dbversion=4.2.17", "test:mongodb:4.4.10": "npm run test:mongodb --dbversion=4.4.10", + "test:mongodb:5.0.5": "npm run test:mongodb --dbversion=5.0.5", "posttest:mongodb": "mongodb-runner stop", - "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.4.10} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", - "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.4.10} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", + "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", + "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", "test": "npm run testonly", - "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.4.10} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop", - "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.4.10} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine", + "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop", + "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine", "start": "node ./bin/parse-server", "prettier": "prettier --write {src,spec}/{**/*,*}.js", "prepare": "npm run build", diff --git a/spec/AudienceRouter.spec.js b/spec/AudienceRouter.spec.js index 208e7834b6..9f0f77896a 100644 --- a/spec/AudienceRouter.spec.js +++ b/spec/AudienceRouter.spec.js @@ -326,22 +326,24 @@ describe('AudiencesRouter', () => { { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) }, { useMasterKey: true } ).then(audience => { - database.collection('test__Audience').updateOne( - { _id: audience.objectId }, - { - $set: { - times_used: 1, - _last_used: now, - }, - }, - {}, - error => { - expect(error).toEqual(null); + database + .collection('test__Audience') + .updateOne( + { _id: audience.objectId }, + { + $set: { + times_used: 1, + _last_used: now, + }, + } + ) + .then(result => { + expect(result).toBeTruthy(); database .collection('test__Audience') .find({ _id: audience.objectId }) .toArray((error, rows) => { - expect(error).toEqual(null); + expect(error).toEqual(undefined); expect(rows[0]['times_used']).toEqual(1); expect(rows[0]['_last_used']).toEqual(now); Parse._request( @@ -361,8 +363,7 @@ describe('AudiencesRouter', () => { done.fail(error); }); }); - } - ); + }); }); }); diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index 996e4c3961..9ea66da6ea 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -3,7 +3,6 @@ const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapte .WinstonLoggerAdapter; const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter') .GridFSBucketAdapter; -const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter').GridStoreAdapter; const Config = require('../lib/Config'); const FilesController = require('../lib/Controllers/FilesController').default; const databaseURI = 'mongodb://localhost:27017/parse'; @@ -24,8 +23,8 @@ const mockAdapter = { describe('FilesController', () => { it('should properly expand objects', done => { const config = Config.get(Parse.applicationId); - const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); - const filesController = new FilesController(gridStoreAdapter); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); + const filesController = new FilesController(gridFSAdapter); const result = filesController.expandFilesInObject(config, function () {}); expect(result).toBeUndefined(); @@ -88,19 +87,19 @@ describe('FilesController', () => { it('should add a unique hash to the file name when the preserveFileName option is false', done => { const config = Config.get(Parse.applicationId); - const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); - spyOn(gridStoreAdapter, 'createFile'); - gridStoreAdapter.createFile.and.returnValue(Promise.resolve()); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); + spyOn(gridFSAdapter, 'createFile'); + gridFSAdapter.createFile.and.returnValue(Promise.resolve()); const fileName = 'randomFileName.pdf'; const regexEscapedFileName = fileName.replace(/\./g, '\\$&'); - const filesController = new FilesController(gridStoreAdapter, null, { + const filesController = new FilesController(gridFSAdapter, null, { preserveFileName: false, }); filesController.createFile(config, fileName); - expect(gridStoreAdapter.createFile).toHaveBeenCalledTimes(1); - expect(gridStoreAdapter.createFile.calls.mostRecent().args[0]).toMatch( + expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1); + expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toMatch( `^.{32}_${regexEscapedFileName}$` ); @@ -109,42 +108,42 @@ describe('FilesController', () => { it('should not add a unique hash to the file name when the preserveFileName option is true', done => { const config = Config.get(Parse.applicationId); - const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); - spyOn(gridStoreAdapter, 'createFile'); - gridStoreAdapter.createFile.and.returnValue(Promise.resolve()); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); + spyOn(gridFSAdapter, 'createFile'); + gridFSAdapter.createFile.and.returnValue(Promise.resolve()); const fileName = 'randomFileName.pdf'; - const filesController = new FilesController(gridStoreAdapter, null, { + const filesController = new FilesController(gridFSAdapter, null, { preserveFileName: true, }); filesController.createFile(config, fileName); - expect(gridStoreAdapter.createFile).toHaveBeenCalledTimes(1); - expect(gridStoreAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName); + expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1); + expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName); done(); }); it('should handle adapter without getMetadata', async () => { - const gridStoreAdapter = new GridFSBucketAdapter(databaseURI); - gridStoreAdapter.getMetadata = null; - const filesController = new FilesController(gridStoreAdapter); + const gridFSAdapter = new GridFSBucketAdapter(databaseURI); + gridFSAdapter.getMetadata = null; + const filesController = new FilesController(gridFSAdapter); const result = await filesController.getMetadata(); expect(result).toEqual({}); }); it('should reject slashes in file names', done => { - const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); const fileName = 'foo/randomFileName.pdf'; - expect(gridStoreAdapter.validateFilename(fileName)).not.toBe(null); + expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null); done(); }); it('should also reject slashes in file names', done => { - const gridStoreAdapter = new GridStoreAdapter('mongodb://localhost:27017/parse'); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); const fileName = 'foo/randomFileName.pdf'; - expect(gridStoreAdapter.validateFilename(fileName)).not.toBe(null); + expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null); done(); }); }); diff --git a/spec/GridFSBucketStorageAdapter.spec.js b/spec/GridFSBucketStorageAdapter.spec.js index 8431d6d7f5..7ffdced2bb 100644 --- a/spec/GridFSBucketStorageAdapter.spec.js +++ b/spec/GridFSBucketStorageAdapter.spec.js @@ -1,4 +1,3 @@ -const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter').GridStoreAdapter; const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter') .GridFSBucketAdapter; const { randomString } = require('../lib/cryptoUtils'); @@ -14,25 +13,13 @@ async function expectMissingFile(gfsAdapter, name) { } } -describe_only_db('mongo')('GridFSBucket and GridStore interop', () => { +describe_only_db('mongo')('GridFSBucket', () => { beforeEach(async () => { - const gsAdapter = new GridStoreAdapter(databaseURI); + const gsAdapter = new GridFSBucketAdapter(databaseURI); const db = await gsAdapter._connect(); await db.dropDatabase(); }); - it('a file created in GridStore should be available in GridFS', async () => { - const gsAdapter = new GridStoreAdapter(databaseURI); - const gfsAdapter = new GridFSBucketAdapter(databaseURI); - await expectMissingFile(gfsAdapter, 'myFileName'); - const originalString = 'abcdefghi'; - await gsAdapter.createFile('myFileName', originalString); - const gsResult = await gsAdapter.getFileData('myFileName'); - expect(gsResult.toString('utf8')).toBe(originalString); - const gfsResult = await gfsAdapter.getFileData('myFileName'); - expect(gfsResult.toString('utf8')).toBe(originalString); - }); - it('should save an encrypted file that can only be decrypted by a GridFS adapter with the encryptionKey', async () => { const unencryptedAdapter = new GridFSBucketAdapter(databaseURI); const encryptedAdapter = new GridFSBucketAdapter( @@ -451,7 +438,7 @@ describe_only_db('mongo')('GridFSBucket and GridStore interop', () => { await db.admin().serverStatus(); expect(false).toBe(true); } catch (e) { - expect(e.message).toEqual('topology was destroyed'); + expect(e.message).toEqual('MongoClient must be connected to perform this operation'); } }); }); diff --git a/spec/GridStoreAdapter.spec.js b/spec/GridStoreAdapter.spec.js deleted file mode 100644 index 145b8e0e6d..0000000000 --- a/spec/GridStoreAdapter.spec.js +++ /dev/null @@ -1,111 +0,0 @@ -const MongoClient = require('mongodb').MongoClient; -const GridStore = require('mongodb').GridStore; - -const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter').GridStoreAdapter; -const Config = require('../lib/Config'); -const FilesController = require('../lib/Controllers/FilesController').default; - -// Small additional tests to improve overall coverage -describe_only_db('mongo')('GridStoreAdapter', () => { - it('should properly instanciate the GridStore when deleting a file', async done => { - const databaseURI = 'mongodb://localhost:27017/parse'; - const config = Config.get(Parse.applicationId); - const gridStoreAdapter = new GridStoreAdapter(databaseURI); - const db = await gridStoreAdapter._connect(); - await db.dropDatabase(); - const filesController = new FilesController(gridStoreAdapter, Parse.applicationId, {}); - - // save original unlink before redefinition - const originalUnlink = GridStore.prototype.unlink; - - let gridStoreMode; - - // new unlink method that will capture the mode in which GridStore was opened - GridStore.prototype.unlink = function () { - // restore original unlink during first call - GridStore.prototype.unlink = originalUnlink; - - gridStoreMode = this.mode; - - return originalUnlink.call(this); - }; - - filesController - .createFile(config, 'myFilename.txt', 'my file content', 'text/plain') - .then(myFile => { - return MongoClient.connect(databaseURI) - .then(client => { - const database = client.db(client.s.options.dbName); - // Verify the existance of the fs.files document - return database - .collection('fs.files') - .count() - .then(count => { - expect(count).toEqual(1); - return { database, client }; - }); - }) - .then(({ database, client }) => { - // Verify the existance of the fs.files document - return database - .collection('fs.chunks') - .count() - .then(count => { - expect(count).toEqual(1); - return client.close(); - }); - }) - .then(() => { - return filesController.deleteFile(config, myFile.name); - }); - }) - .then(() => { - return MongoClient.connect(databaseURI) - .then(client => { - const database = client.db(client.s.options.dbName); - // Verify the existance of the fs.files document - return database - .collection('fs.files') - .count() - .then(count => { - expect(count).toEqual(0); - return { database, client }; - }); - }) - .then(({ database, client }) => { - // Verify the existance of the fs.files document - return database - .collection('fs.chunks') - .count() - .then(count => { - expect(count).toEqual(0); - return client.close(); - }); - }); - }) - .then(() => { - // Verify that gridStore was opened in read only mode - expect(gridStoreMode).toEqual('r'); - - done(); - }) - .catch(fail); - }); - - it('handleShutdown, close connection', async () => { - const databaseURI = 'mongodb://localhost:27017/parse'; - const gridStoreAdapter = new GridStoreAdapter(databaseURI); - - const db = await gridStoreAdapter._connect(); - const status = await db.admin().serverStatus(); - expect(status.connections.current > 0).toEqual(true); - - await gridStoreAdapter.handleShutdown(); - try { - await db.admin().serverStatus(); - expect(false).toBe(true); - } catch (e) { - expect(e.message).toEqual('topology was destroyed'); - } - }); -}); diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index f6d2866417..a31a6134f7 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -1,7 +1,7 @@ 'use strict'; const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; -const { MongoClient } = require('mongodb'); +const { MongoClient, Collection } = require('mongodb'); const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; const request = require('../lib/request'); const Config = require('../lib/Config'); @@ -101,7 +101,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { done.fail('Find succeeded despite taking too long!'); }, err => { - expect(err.name).toEqual('MongoError'); + expect(err.name).toEqual('MongoServerError'); expect(err.code).toEqual(50); expect(err.message).toMatch('operation exceeded time limit'); done(); @@ -283,7 +283,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { await adapter.database.admin().serverStatus(); expect(false).toBe(true); } catch (e) { - expect(e.message).toEqual('topology was destroyed'); + expect(e.message).toEqual('MongoClient must be connected to perform this operation'); } }); @@ -392,8 +392,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { const myObject = new Parse.Object('MyObject'); await myObject.save(); - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough(); + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); await request({ method: 'POST', @@ -412,9 +411,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); let found = false; - databaseAdapter.database.serverConfig.command.calls.all().forEach(call => { + Collection.prototype.findOneAndUpdate.calls.all().forEach(call => { found = true; - expect(call.args[2].session.transaction.state).not.toBe('NO_TRANSACTION'); + expect(call.args[2].session.transaction.state).toBe('TRANSACTION_COMMITTED'); }); expect(found).toBe(true); }); @@ -423,8 +422,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { const myObject = new Parse.Object('MyObject'); await myObject.save(); - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough(); + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); await request({ method: 'POST', @@ -443,9 +441,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); let found = false; - databaseAdapter.database.serverConfig.command.calls.all().forEach(call => { + Collection.prototype.findOneAndUpdate.calls.all().forEach(call => { found = true; - expect(call.args[2].session).toBe(undefined); + expect(call.args[2].session).toBeFalsy(); }); expect(found).toBe(true); }); @@ -454,8 +452,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { const myObject = new Parse.Object('MyObject'); await myObject.save(); - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough(); + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); await request({ method: 'POST', @@ -473,9 +470,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); let found = false; - databaseAdapter.database.serverConfig.command.calls.all().forEach(call => { + Collection.prototype.findOneAndUpdate.calls.all().forEach(call => { found = true; - expect(call.args[2].session).toBe(undefined); + expect(call.args[2].session).toBeFalsy(); }); expect(found).toBe(true); }); @@ -484,8 +481,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { const myObject = new Parse.Object('MyObject'); await myObject.save(); - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough(); + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); await request({ method: 'PUT', @@ -495,30 +491,28 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); let found = false; - databaseAdapter.database.serverConfig.command.calls.all().forEach(call => { + Collection.prototype.findOneAndUpdate.calls.all().forEach(call => { found = true; - expect(call.args[2].session).toBe(undefined); + expect(call.args[2].session).toBeFalsy(); }); expect(found).toBe(true); }); it('should not use transactions when using SDK insert', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'insert').and.callThrough(); + spyOn(Collection.prototype, 'insertOne').and.callThrough(); const myObject = new Parse.Object('MyObject'); await myObject.save(); - const calls = databaseAdapter.database.serverConfig.insert.calls.all(); + const calls = Collection.prototype.insertOne.calls.all(); expect(calls.length).toBeGreaterThan(0); calls.forEach(call => { - expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION'); + expect(call.args[1].session).toBeFalsy(); }); }); it('should not use transactions when using SDK update', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'update').and.callThrough(); + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); const myObject = new Parse.Object('MyObject'); await myObject.save(); @@ -526,26 +520,25 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { myObject.set('myAttribute', 'myValue'); await myObject.save(); - const calls = databaseAdapter.database.serverConfig.update.calls.all(); + const calls = Collection.prototype.findOneAndUpdate.calls.all(); expect(calls.length).toBeGreaterThan(0); calls.forEach(call => { - expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION'); + expect(call.args[2].session).toBeFalsy(); }); }); it('should not use transactions when using SDK delete', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'remove').and.callThrough(); + spyOn(Collection.prototype, 'deleteMany').and.callThrough(); const myObject = new Parse.Object('MyObject'); await myObject.save(); await myObject.destroy(); - const calls = databaseAdapter.database.serverConfig.remove.calls.all(); + const calls = Collection.prototype.deleteMany.calls.all(); expect(calls.length).toBeGreaterThan(0); calls.forEach(call => { - expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION'); + expect(call.args[1].session).toBeFalsy(); }); }); }); diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 6541092b97..32a526132a 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -32,7 +32,7 @@ const { } = require('graphql'); const { ParseServer } = require('../'); const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer'); -const ReadPreference = require('mongodb').ReadPreference; +const { ReadPreference, Collection } = require('mongodb'); const { v4: uuidv4 } = require('uuid'); function handleError(e) { @@ -4473,8 +4473,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const databaseAdapter = parseServer.config.databaseController.adapter; - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -4498,13 +4497,13 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) { + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY); - } else if (call.args[0].ns.collection.indexOf('_User') >= 0) { + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY); + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); } }); @@ -4520,8 +4519,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const databaseAdapter = parseServer.config.databaseController.adapter; - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -4545,13 +4543,13 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) { + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY); - } else if (call.args[0].ns.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY); + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); } }); @@ -4564,8 +4562,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const databaseAdapter = parseServer.config.databaseController.adapter; - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -4592,13 +4589,13 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) { + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY); - } else if (call.args[0].ns.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST); + expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); } }); @@ -5456,8 +5453,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const databaseAdapter = parseServer.config.databaseController.adapter; - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -5482,13 +5478,13 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) { + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY); - } else if (call.args[0].ns.collection.indexOf('_User') >= 0) { + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY); + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); } }); @@ -5501,8 +5497,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const databaseAdapter = parseServer.config.databaseController.adapter; - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -5527,13 +5522,13 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) { + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY); - } else if (call.args[0].ns.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY); + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); } }); @@ -5546,8 +5541,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const databaseAdapter = parseServer.config.databaseController.adapter; - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -5574,13 +5568,13 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) { + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY); - } else if (call.args[0].ns.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST); + expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); } }); @@ -5594,8 +5588,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const databaseAdapter = parseServer.config.databaseController.adapter; - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -5632,13 +5625,13 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) { + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY); - } else if (call.args[0].ns.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST); + expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); } }); diff --git a/spec/ReadPreferenceOption.spec.js b/spec/ReadPreferenceOption.spec.js index f2bc328d99..535c35c8b0 100644 --- a/spec/ReadPreferenceOption.spec.js +++ b/spec/ReadPreferenceOption.spec.js @@ -1,9 +1,8 @@ 'use strict'; const Parse = require('parse/node'); -const ReadPreference = require('mongodb').ReadPreference; +const { ReadPreference, Collection } = require('mongodb'); const request = require('../lib/request'); -const Config = require('../lib/Config'); function waitForReplication() { return new Promise(function (resolve) { @@ -13,8 +12,6 @@ function waitForReplication() { describe_only_db('mongo')('Read preference option', () => { it('should find in primary by default', done => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); @@ -22,7 +19,7 @@ describe_only_db('mongo')('Read preference option', () => { Parse.Object.saveAll([obj0, obj1]) .then(() => { - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); const query = new Parse.Query('MyObject'); query.equalTo('boolKey', false); @@ -31,10 +28,10 @@ describe_only_db('mongo')('Read preference option', () => { expect(results.length).toBe(1); expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY); + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); } }); @@ -58,15 +55,13 @@ describe_only_db('mongo')('Read preference option', () => { databaseAdapter: new MongoStorageAdapter(adapterOptions), }); - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); const query = new Parse.Query('MyObject'); query.equalTo('boolKey', false); @@ -76,10 +71,10 @@ describe_only_db('mongo')('Read preference option', () => { expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = true; - expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST); + expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); } }); @@ -87,15 +82,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference in the beforeFind trigger', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY'; @@ -110,9 +103,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -120,15 +113,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should check read preference as case insensitive', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'sEcOnDarY'; @@ -144,9 +135,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -154,15 +145,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference in the beforeFind trigger even changing query', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.query.equalTo('boolKey', true); @@ -178,9 +167,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(results[0].get('boolKey')).toBe(true); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -188,15 +177,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference in the beforeFind trigger even returning query', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY'; @@ -216,9 +203,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(results[0].get('boolKey')).toBe(true); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -226,15 +213,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference in the beforeFind trigger even returning promise', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY'; @@ -253,9 +238,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(results[0].get('boolKey')).toBe(true); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -263,15 +248,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference to PRIMARY_PREFERRED', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'PRIMARY_PREFERRED'; @@ -286,9 +269,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -296,15 +279,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference to SECONDARY_PREFERRED', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY_PREFERRED'; @@ -319,9 +300,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -329,15 +310,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference to NEAREST', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'NEAREST'; @@ -352,9 +331,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -362,15 +341,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference for GET', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY'; @@ -383,9 +360,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(result.get('boolKey')).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -393,15 +370,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference for GET using API', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY'; @@ -421,9 +396,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(body.boolKey).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -431,15 +406,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference for GET directly from API', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const response = await request({ @@ -454,9 +427,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(response.data.boolKey).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -464,15 +437,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference for GET using API through the beforeFind overriding API option', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY_PREFERRED'; @@ -491,9 +462,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(response.data.boolKey).toBe(false); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -501,15 +472,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference for FIND using API through beforeFind trigger', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY'; @@ -528,9 +497,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(response.data.results.length).toEqual(2); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -538,15 +507,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference for FIND directly from API', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const response = await request({ @@ -561,9 +528,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(response.data.results.length).toEqual(2); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -571,15 +538,13 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change read preference for FIND using API through the beforeFind overriding API option', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY_PREFERRED'; @@ -598,9 +563,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(response.data.results.length).toEqual(2); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -608,15 +573,13 @@ describe_only_db('mongo')('Read preference option', () => { }); xit('should change read preference for count', done => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject'); obj1.set('boolKey', true); Parse.Object.saveAll([obj0, obj1]).then(() => { - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject', req => { req.readPreference = 'SECONDARY'; @@ -631,9 +594,9 @@ describe_only_db('mongo')('Read preference option', () => { expect(result).toBe(1); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); @@ -659,17 +622,16 @@ describe_only_db('mongo')('Read preference option', () => { await waitForReplication(); // Spy on DB adapter - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'startSession').and.callThrough(); + spyOn(Collection.prototype, 'aggregate').and.callThrough(); // Query const query = new Parse.Query('MyObject'); const results = await query.aggregate([{ match: { boolKey: false } }]); // Validate expect(results.length).toBe(1); let readPreference = null; - databaseAdapter.database.serverConfig.startSession.calls.all().forEach(call => { - if (call.args[0].owner.ns.indexOf('MyObject') > -1) { - readPreference = call.args[0].owner.operation.readPreference.mode; + Collection.prototype.aggregate.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') > -1) { + readPreference = call.args[1].readPreference; } }); expect(readPreference).toEqual(ReadPreference.SECONDARY); @@ -685,8 +647,7 @@ describe_only_db('mongo')('Read preference option', () => { await waitForReplication(); // Spy on DB adapter - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); // Query const query = new Parse.Query('MyObject'); query.equalTo('boolKey', false); @@ -695,9 +656,9 @@ describe_only_db('mongo')('Read preference option', () => { // Validate expect(results.length).toBe(1); let myObjectReadPreference = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject') >= 0) { - myObjectReadPreference = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + myObjectReadPreference = call.args[1].readPreference; } }); expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); @@ -713,8 +674,7 @@ describe_only_db('mongo')('Read preference option', () => { await waitForReplication(); // Spy on DB adapter - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - spyOn(databaseAdapter.database.serverConfig, 'startSession').and.callThrough(); + spyOn(Collection.prototype, 'aggregate').and.callThrough(); // Query const query = new Parse.Query('MyObject'); query.readPreference('SECONDARY'); @@ -722,17 +682,15 @@ describe_only_db('mongo')('Read preference option', () => { // Validate expect(results.length).toBe(1); let readPreference = null; - databaseAdapter.database.serverConfig.startSession.calls.all().forEach(call => { - if (call.args[0].owner.ns.indexOf('MyObject') > -1) { - readPreference = call.args[0].owner.operation.readPreference.mode; + Collection.prototype.aggregate.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject') > -1) { + readPreference = call.args[1].readPreference; } }); expect(readPreference).toEqual(ReadPreference.SECONDARY); }); it('should find includes in same replica of readPreference by default', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject0'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject1'); @@ -743,7 +701,7 @@ describe_only_db('mongo')('Read preference option', () => { obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject2', req => { req.readPreference = 'SECONDARY'; @@ -765,15 +723,15 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) { - myObjectReadPreference0 = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + myObjectReadPreference0 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) { - myObjectReadPreference1 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + myObjectReadPreference1 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) { - myObjectReadPreference2 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -783,8 +741,6 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change includes read preference', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject0'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject1'); @@ -795,7 +751,7 @@ describe_only_db('mongo')('Read preference option', () => { obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject2', req => { req.readPreference = 'SECONDARY_PREFERRED'; @@ -818,15 +774,15 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) { - myObjectReadPreference0 = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + myObjectReadPreference0 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) { - myObjectReadPreference1 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + myObjectReadPreference1 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) { - myObjectReadPreference2 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -836,8 +792,6 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change includes read preference when finding through API', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject0'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject1'); @@ -848,7 +802,7 @@ describe_only_db('mongo')('Read preference option', () => { obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const response = await request({ @@ -873,15 +827,15 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) { - myObjectReadPreference0 = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + myObjectReadPreference0 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) { - myObjectReadPreference1 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + myObjectReadPreference1 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) { - myObjectReadPreference2 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -891,8 +845,6 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change includes read preference when getting through API', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject0'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject1'); @@ -903,7 +855,7 @@ describe_only_db('mongo')('Read preference option', () => { obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const response = await request({ @@ -929,15 +881,15 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) { - myObjectReadPreference0 = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + myObjectReadPreference0 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) { - myObjectReadPreference1 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + myObjectReadPreference1 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) { - myObjectReadPreference2 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -947,8 +899,6 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should find subqueries in same replica of readPreference by default', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject0'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject1'); @@ -959,7 +909,7 @@ describe_only_db('mongo')('Read preference option', () => { obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject2', req => { req.readPreference = 'SECONDARY'; @@ -982,15 +932,15 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) { - myObjectReadPreference0 = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + myObjectReadPreference0 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) { - myObjectReadPreference1 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + myObjectReadPreference1 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) { - myObjectReadPreference2 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1000,8 +950,6 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change subqueries read preference when using matchesQuery', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject0'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject1'); @@ -1012,7 +960,7 @@ describe_only_db('mongo')('Read preference option', () => { obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject2', req => { req.readPreference = 'SECONDARY_PREFERRED'; @@ -1036,15 +984,15 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) { - myObjectReadPreference0 = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + myObjectReadPreference0 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) { - myObjectReadPreference1 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + myObjectReadPreference1 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) { - myObjectReadPreference2 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1054,8 +1002,6 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change subqueries read preference when using doesNotMatchQuery', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject0'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject1'); @@ -1066,7 +1012,7 @@ describe_only_db('mongo')('Read preference option', () => { obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject2', req => { req.readPreference = 'SECONDARY_PREFERRED'; @@ -1090,15 +1036,15 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) { - myObjectReadPreference0 = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + myObjectReadPreference0 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) { - myObjectReadPreference1 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + myObjectReadPreference1 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) { - myObjectReadPreference2 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1108,8 +1054,6 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject0'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject1'); @@ -1120,7 +1064,7 @@ describe_only_db('mongo')('Read preference option', () => { obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); Parse.Cloud.beforeFind('MyObject2', req => { req.readPreference = 'SECONDARY_PREFERRED'; @@ -1145,15 +1089,15 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) { - myObjectReadPreference0 = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + myObjectReadPreference0 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) { - myObjectReadPreference1 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + myObjectReadPreference1 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) { - myObjectReadPreference2 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1163,8 +1107,6 @@ describe_only_db('mongo')('Read preference option', () => { }); it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery to find through API', async () => { - const databaseAdapter = Config.get(Parse.applicationId).database.adapter; - const obj0 = new Parse.Object('MyObject0'); obj0.set('boolKey', false); const obj1 = new Parse.Object('MyObject1'); @@ -1175,7 +1117,7 @@ describe_only_db('mongo')('Read preference option', () => { obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const whereString = JSON.stringify({ @@ -1215,15 +1157,15 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; - databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => { - if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) { - myObjectReadPreference0 = call.args[0].options.readPreference.mode; + Collection.prototype.find.calls.all().forEach(call => { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + myObjectReadPreference0 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) { - myObjectReadPreference1 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + myObjectReadPreference1 = call.args[1].readPreference; } - if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) { - myObjectReadPreference2 = call.args[0].options.readPreference.mode; + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + myObjectReadPreference2 = call.args[1].readPreference; } }); diff --git a/src/Adapters/Files/GridFSBucketAdapter.js b/src/Adapters/Files/GridFSBucketAdapter.js index 84876fad6f..06896e73b5 100644 --- a/src/Adapters/Files/GridFSBucketAdapter.js +++ b/src/Adapters/Files/GridFSBucketAdapter.js @@ -1,6 +1,6 @@ /** GridFSBucketAdapter - Stores files in Mongo using GridStore + Stores files in Mongo using GridFS Requires the database adapter to be based on mongoclient @flow weak diff --git a/src/Adapters/Files/GridStoreAdapter.js b/src/Adapters/Files/GridStoreAdapter.js index ca3166af98..39d1ca41c6 100644 --- a/src/Adapters/Files/GridStoreAdapter.js +++ b/src/Adapters/Files/GridStoreAdapter.js @@ -1,181 +1,4 @@ -/** - GridStoreAdapter - Stores files in Mongo using GridStore - Requires the database adapter to be based on mongoclient - (GridStore is deprecated, Please use GridFSBucket instead) - - @flow weak - */ - -// @flow-disable-next -import { MongoClient, GridStore, Db } from 'mongodb'; -import { FilesAdapter, validateFilename } from './FilesAdapter'; -import defaults from '../../defaults'; - -export class GridStoreAdapter extends FilesAdapter { - _databaseURI: string; - _connectionPromise: Promise; - _mongoOptions: Object; - - constructor(mongoDatabaseURI = defaults.DefaultMongoURI, mongoOptions = {}) { - super(); - this._databaseURI = mongoDatabaseURI; - - const defaultMongoOptions = { - useNewUrlParser: true, - useUnifiedTopology: true, - }; - this._mongoOptions = Object.assign(defaultMongoOptions, mongoOptions); - } - - _connect() { - if (!this._connectionPromise) { - this._connectionPromise = MongoClient.connect(this._databaseURI, this._mongoOptions).then( - client => { - this._client = client; - return client.db(client.s.options.dbName); - } - ); - } - return this._connectionPromise; - } - - // For a given config object, filename, and data, store a file - // Returns a promise - createFile(filename: string, data) { - return this._connect() - .then(database => { - const gridStore = new GridStore(database, filename, 'w'); - return gridStore.open(); - }) - .then(gridStore => { - return gridStore.write(data); - }) - .then(gridStore => { - return gridStore.close(); - }); - } - - deleteFile(filename: string) { - return this._connect() - .then(database => { - const gridStore = new GridStore(database, filename, 'r'); - return gridStore.open(); - }) - .then(gridStore => { - return gridStore.unlink(); - }) - .then(gridStore => { - return gridStore.close(); - }); - } - - getFileData(filename: string) { - return this._connect() - .then(database => { - return GridStore.exist(database, filename).then(() => { - const gridStore = new GridStore(database, filename, 'r'); - return gridStore.open(); - }); - }) - .then(gridStore => { - return gridStore.read(); - }); - } - - getFileLocation(config, filename) { - return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename); - } - - async handleFileStream(filename: string, req, res, contentType) { - const stream = await this._connect().then(database => { - return GridStore.exist(database, filename).then(() => { - const gridStore = new GridStore(database, filename, 'r'); - return gridStore.open(); - }); - }); - handleRangeRequest(stream, req, res, contentType); - } - - handleShutdown() { - if (!this._client) { - return Promise.resolve(); - } - return this._client.close(false); - } - - validateFilename(filename) { - return validateFilename(filename); - } -} - -// handleRangeRequest is licensed under Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0/). -// Author: LEROIB at weightingformypizza (https://weightingformypizza.wordpress.com/2015/06/24/stream-html5-media-content-like-video-audio-from-mongodb-using-express-and-gridstore/). -function handleRangeRequest(stream, req, res, contentType) { - const buffer_size = 1024 * 1024; //1024Kb - // Range request, partial stream the file - const parts = req - .get('Range') - .replace(/bytes=/, '') - .split('-'); - let [start, end] = parts; - const notEnded = !end && end !== 0; - const notStarted = !start && start !== 0; - // No end provided, we want all bytes - if (notEnded) { - end = stream.length - 1; - } - // No start provided, we're reading backwards - if (notStarted) { - start = stream.length - end; - end = start + end - 1; - } - - // Data exceeds the buffer_size, cap - if (end - start >= buffer_size) { - end = start + buffer_size - 1; - } - - const contentLength = end - start + 1; - - res.writeHead(206, { - 'Content-Range': 'bytes ' + start + '-' + end + '/' + stream.length, - 'Accept-Ranges': 'bytes', - 'Content-Length': contentLength, - 'Content-Type': contentType, - }); - - stream.seek(start, function () { - // Get gridFile stream - const gridFileStream = stream.stream(true); - let bufferAvail = 0; - let remainingBytesToWrite = contentLength; - let totalBytesWritten = 0; - // Write to response - gridFileStream.on('data', function (data) { - bufferAvail += data.length; - if (bufferAvail > 0) { - // slice returns the same buffer if overflowing - // safe to call in any case - const buffer = data.slice(0, remainingBytesToWrite); - // Write the buffer - res.write(buffer); - // Increment total - totalBytesWritten += buffer.length; - // Decrement remaining - remainingBytesToWrite -= data.length; - // Decrement the available buffer - bufferAvail -= buffer.length; - } - // In case of small slices, all values will be good at that point - // we've written enough, end... - if (totalBytesWritten >= contentLength) { - stream.close(); - res.end(); - this.destroy(); - } - }); - }); -} - -export default GridStoreAdapter; +// Note: GridStore was replaced by GridFSBucketAdapter by default in 2018 by @flovilmart +throw new Error( + 'GridStoreAdapter: GridStore is no longer supported by parse server and mongodb, use GridFSBucketAdapter instead.' +); diff --git a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js index 78284503fc..20e3eec325 100644 --- a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js +++ b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js @@ -177,7 +177,7 @@ class MongoSchemaCollection { insertSchema(schema: any) { return this._collection .insertOne(schema) - .then(result => mongoSchemaToParseSchema(result.ops[0])) + .then(() => mongoSchemaToParseSchema(schema)) .catch(error => { if (error.code === 11000) { //Mongo's duplicate key error diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index c1fe63ba9b..93e23b91c3 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -479,6 +479,7 @@ export class MongoStorageAdapter implements StorageAdapter { const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema); return this._adaptiveCollection(className) .then(collection => collection.insertOne(mongoObject, transactionalSession)) + .then(() => ({ ops: [mongoObject] })) .catch(error => { if (error.code === 11000) { // Duplicate value @@ -517,8 +518,8 @@ export class MongoStorageAdapter implements StorageAdapter { }) .catch(err => this.handleError(err)) .then( - ({ result }) => { - if (result.n === 0) { + ({ deletedCount }) => { + if (deletedCount === 0) { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); } return Promise.resolve();