From 2abccbb273f3f01edcf54f9f66f2ff28d9d4e27e Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Mon, 6 Jun 2016 13:47:11 -0700 Subject: [PATCH 01/44] index on unique-indexes: c454180 Revert "Log objects rather than JSON stringified objects (#1922)" --- spec/ParseAPI.spec.js | 79 +++++++++++++++++++++++-------------------- src/RestWrite.js | 10 ++++-- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 6696568110..a4a87869f1 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -81,44 +81,51 @@ describe('miscellaneous', function() { .catch(done); }); - it('ensure that email is uniquely indexed', done => { - let numCreated = 0; - let numFailed = 0; - - let user1 = new Parse.User(); - user1.setPassword('asdf'); - user1.setUsername('u1'); - user1.setEmail('dupe@dupe.dupe'); - let p1 = user1.signUp(); - p1.then(user => { - numCreated++; - expect(numCreated).toEqual(1); - }, error => { - numFailed++; - expect(numFailed).toEqual(1); - expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); - }); + fit('ensure that email is uniquely indexed', done => { + DatabaseAdapter._indexBuildsCompleted('test') + .then(() => { + let numCreated = 0; + let numFailed = 0; + + let user1 = new Parse.User(); + user1.setPassword('asdf'); + user1.setUsername('u1'); + user1.setEmail('dupe@dupe.dupe'); + let p1 = user1.signUp(); + p1.then(user => { + numCreated++; + expect(numCreated).toEqual(1); + }, error => { + numFailed++; + console.log(error); + expect(numFailed).toEqual(1); + expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); + }); - let user2 = new Parse.User(); - user2.setPassword('asdf'); - user2.setUsername('u2'); - user2.setEmail('dupe@dupe.dupe'); - let p2 = user2.signUp(); - p2.then(user => { - numCreated++; - expect(numCreated).toEqual(1); - }, error => { - numFailed++; - expect(numFailed).toEqual(1); - expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); - }); + let user2 = new Parse.User(); + user2.setPassword('asdf'); + user2.setUsername('u2'); + user2.setEmail('dupe@dupe.dupe'); + let p2 = user2.signUp(); + p2.then(user => { + numCreated++; + expect(numCreated).toEqual(1); + }, error => { + numFailed++; + console.log(error); + expect(numFailed).toEqual(1); + expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); + }); - Parse.Promise.when([p1, p2]) - .then(() => { - fail('one of the users should not have been created'); - done(); - }) - .catch(done); + Parse.Promise.when([p1, p2]) + .then(() => { + fail('one of the users should not have been created'); + done(); + }) + .catch(error => { + fail('index build failed') + done(); + }); }); it('ensure that if people already have duplicate users, they can still sign up new users', done => { diff --git a/src/RestWrite.js b/src/RestWrite.js index 78478025ab..6b18dd28c8 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -388,11 +388,15 @@ RestWrite.prototype.transformUser = function() { if (results.length > 0) { throw new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.'); } - // We updated the email, send a new validation - this.storage['sendVerificationEmail'] = true; - this.config.userController.setEmailVerifyToken(this.data); + return; }); }) + .then(() => { + // We updated the email, send a new validation + this.storage['sendVerificationEmail'] = true; + this.config.userController.setEmailVerifyToken(this.data); + return; + }) }; RestWrite.prototype.createSessionTokenIfNeeded = function() { From 7a4fa7b87ae62a90545f9f42211e1eb0e58f09cc Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Mon, 6 Jun 2016 20:52:58 -0700 Subject: [PATCH 02/44] Start dealing with test shittyness --- spec/ParseAPI.spec.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index a4a87869f1..7d897db9da 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -129,9 +129,8 @@ describe('miscellaneous', function() { }); it('ensure that if people already have duplicate users, they can still sign up new users', done => { - reconfigureServer({}) // Remove existing data to clear out unique index - .then(TestUtils.destroyAllDataPermanently) + TestUtils.destroyAllDataPermanently() .then(() => { let adapter = new MongoStorageAdapter({ uri: 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase', @@ -166,16 +165,12 @@ describe('miscellaneous', function() { }); it('ensure that if people already have duplicate emails, they can still sign up new users', done => { - reconfigureServer({}) // Wipe out existing database with unique index so we can create a duplicate user - .then(TestUtils.destroyAllDataPermanently) + TestUtils.destroyAllDataPermanently() .then(() => { - let adapter = new MongoStorageAdapter({ - uri: 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase', - collectionPrefix: 'test_', - }); - adapter.createObject('_User', { objectId: 'x', email: 'a@b.c' }, requiredUserFields) - .then(() => adapter.createObject('_User', { objectId: 'y', email: 'a@b.c' }, requiredUserFields)) + let config = new Config('test'); + config.database.adapter.createObject('_User', { objectId: 'x', email: 'a@b.c' }, requiredUserFields) + .then(() => config.database.adapter.createObject('_User', { objectId: 'y', email: 'a@b.c' }, requiredUserFields)) .then(() => { let user = new Parse.User(); user.setPassword('asdf'); From 2243f04c61f1de00ee9e7d025b2c9721ddd4aa12 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 7 Jun 2016 13:02:26 -0700 Subject: [PATCH 03/44] Make specific server config for tests async --- spec/ParseAPI.spec.js | 82 ++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 7d897db9da..dfe60d5b3e 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -81,51 +81,47 @@ describe('miscellaneous', function() { .catch(done); }); - fit('ensure that email is uniquely indexed', done => { - DatabaseAdapter._indexBuildsCompleted('test') - .then(() => { - let numCreated = 0; - let numFailed = 0; - - let user1 = new Parse.User(); - user1.setPassword('asdf'); - user1.setUsername('u1'); - user1.setEmail('dupe@dupe.dupe'); - let p1 = user1.signUp(); - p1.then(user => { - numCreated++; - expect(numCreated).toEqual(1); - }, error => { - numFailed++; - console.log(error); - expect(numFailed).toEqual(1); - expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); - }); + it('ensure that email is uniquely indexed', done => { + let numCreated = 0; + let numFailed = 0; - let user2 = new Parse.User(); - user2.setPassword('asdf'); - user2.setUsername('u2'); - user2.setEmail('dupe@dupe.dupe'); - let p2 = user2.signUp(); - p2.then(user => { - numCreated++; - expect(numCreated).toEqual(1); - }, error => { - numFailed++; - console.log(error); - expect(numFailed).toEqual(1); - expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); - }); + let user1 = new Parse.User(); + user1.setPassword('asdf'); + user1.setUsername('u1'); + user1.setEmail('dupe@dupe.dupe'); + let p1 = user1.signUp(); + p1.then(user => { + numCreated++; + expect(numCreated).toEqual(1); + }, error => { + numFailed++; + expect(numFailed).toEqual(1); + expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); + }); - Parse.Promise.when([p1, p2]) - .then(() => { - fail('one of the users should not have been created'); - done(); - }) - .catch(error => { - fail('index build failed') - done(); - }); + let user2 = new Parse.User(); + user2.setPassword('asdf'); + user2.setUsername('u2'); + user2.setEmail('dupe@dupe.dupe'); + let p2 = user2.signUp(); + p2.then(user => { + numCreated++; + expect(numCreated).toEqual(1); + }, error => { + numFailed++; + expect(numFailed).toEqual(1); + expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); + }); + + Parse.Promise.when([p1, p2]) + .then(() => { + fail('one of the users should not have been created'); + done(); + }) + .catch(error => { + fail('index build failed') + done(); + }); }); it('ensure that if people already have duplicate users, they can still sign up new users', done => { From b0d70776aaf90618f427d115367194b9dc6ae8d2 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 7 Jun 2016 16:36:11 -0700 Subject: [PATCH 04/44] Fix email validation --- src/RestWrite.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/RestWrite.js b/src/RestWrite.js index 6b18dd28c8..78478025ab 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -388,15 +388,11 @@ RestWrite.prototype.transformUser = function() { if (results.length > 0) { throw new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.'); } - return; + // We updated the email, send a new validation + this.storage['sendVerificationEmail'] = true; + this.config.userController.setEmailVerifyToken(this.data); }); }) - .then(() => { - // We updated the email, send a new validation - this.storage['sendVerificationEmail'] = true; - this.config.userController.setEmailVerifyToken(this.data); - return; - }) }; RestWrite.prototype.createSessionTokenIfNeeded = function() { From 0ad3b11fd9874c14009042475f4160f4b0bebf10 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 7 Jun 2016 22:16:28 -0700 Subject: [PATCH 05/44] Fix broken cloud code --- spec/helper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/helper.js b/spec/helper.js index b5d000ef00..81cfdd06ef 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -13,7 +13,6 @@ var TestUtils = require('../src/index').TestUtils; var MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter'); const GridStoreAdapter = require('../src/Adapters/Files/GridStoreAdapter').GridStoreAdapter; - var port = 8378; let mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; From 88d731855089f8c00559ede9e87863493c90acfe Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 7 Jun 2016 22:56:37 -0700 Subject: [PATCH 06/44] Save callback to variable --- spec/helper.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/helper.js b/spec/helper.js index 81cfdd06ef..ba6105e7a5 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -69,7 +69,7 @@ server.on('connection', connection => { // Allows testing specific configurations of Parse Server const reconfigureServer = changedConfiguration => { return new Promise((resolve, reject) => { - server.close(() => { + const startNewServer = () => { try { let newConfiguration = Object.assign({}, defaultConfiguration, changedConfiguration, { __indexBuildCompletionCallbackForTests: indexBuildPromise => indexBuildPromise.then(resolve, reject) @@ -88,7 +88,8 @@ const reconfigureServer = changedConfiguration => { } catch(error) { reject(error); } - }); + } + server.close(startNewServer); }); } From 5c86841c7996a24788bf4e6d9cb2b1f69df37aa7 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 7 Jun 2016 23:19:55 -0700 Subject: [PATCH 07/44] undo --- spec/helper.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/helper.js b/spec/helper.js index ba6105e7a5..81cfdd06ef 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -69,7 +69,7 @@ server.on('connection', connection => { // Allows testing specific configurations of Parse Server const reconfigureServer = changedConfiguration => { return new Promise((resolve, reject) => { - const startNewServer = () => { + server.close(() => { try { let newConfiguration = Object.assign({}, defaultConfiguration, changedConfiguration, { __indexBuildCompletionCallbackForTests: indexBuildPromise => indexBuildPromise.then(resolve, reject) @@ -88,8 +88,7 @@ const reconfigureServer = changedConfiguration => { } catch(error) { reject(error); } - } - server.close(startNewServer); + }); }); } From 84b4785677ce84563379501e71a98fc697692866 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 12:08:06 -0700 Subject: [PATCH 08/44] Remove adaptiveCollection --- spec/ParseGlobalConfig.spec.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/ParseGlobalConfig.spec.js b/spec/ParseGlobalConfig.spec.js index 534527a5bf..e2eb0a0965 100644 --- a/spec/ParseGlobalConfig.spec.js +++ b/spec/ParseGlobalConfig.spec.js @@ -7,9 +7,12 @@ let Config = require('../src/Config'); describe('a GlobalConfig', () => { beforeEach(done => { let config = new Config('test'); - config.database.adapter.adaptiveCollection('_GlobalConfig') - .then(coll => coll.upsertOne({ '_id': 1 }, { $set: { params: { companies: ['US', 'DK'] } } })) - .then(() => { done(); }); + config.database.adapter.upsertOneObject( + '_GlobalConfig', + { objectId: 1 }, + { fields: { params: { __type: 'String' } } }, + { params: { companies: ['US', 'DK'] } } + ).then(done); }); it('can be retrieved', (done) => { From f617b9a7b45e6efeba4b47c6b945dba489acb9a1 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 12:10:12 -0700 Subject: [PATCH 09/44] Remove an adaptiveCollection use --- spec/ParseGlobalConfig.spec.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/spec/ParseGlobalConfig.spec.js b/spec/ParseGlobalConfig.spec.js index e2eb0a0965..c1d23b5623 100644 --- a/spec/ParseGlobalConfig.spec.js +++ b/spec/ParseGlobalConfig.spec.js @@ -93,22 +93,23 @@ describe('a GlobalConfig', () => { it('failed getting config when it is missing', (done) => { let config = new Config('test'); - config.database.adapter.adaptiveCollection('_GlobalConfig') - .then(coll => coll.deleteOne({ '_id': 1 })) - .then(() => { - request.get({ - url : 'http://localhost:8378/1/config', - json : true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key' : 'test' - } - }, (error, response, body) => { - expect(response.statusCode).toEqual(200); - expect(body.params).toEqual({}); - done(); - }); + config.database.adapter.deleteObjectsByQuery( + '_GlobalConfig', + { objectId: 1 }, + { fields: { params: { __type: 'String' } } } + ).then(() => { + request.get({ + url : 'http://localhost:8378/1/config', + json : true, + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key' : 'test' + } + }, (error, response, body) => { + expect(response.statusCode).toEqual(200); + expect(body.params).toEqual({}); + done(); }); + }); }); - }); From 1f685bcd490f3db8ac64698a5bbd6ce9fdb182be Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 12:19:23 -0700 Subject: [PATCH 10/44] Remove an adaptiveCollection --- spec/ValidationAndPasswordsReset.spec.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js index 782a3921dc..1fdfd95279 100644 --- a/spec/ValidationAndPasswordsReset.spec.js +++ b/spec/ValidationAndPasswordsReset.spec.js @@ -561,9 +561,8 @@ describe("Password Reset", () => { Parse.User.logIn("zxcv", "hello").then(function(user){ let config = new Config('test'); - config.database.adapter.adaptiveCollection('_User') - .then(coll => coll.find({ 'username': 'zxcv' }, { limit: 1 })) - .then((results) => { + config.database.adapter.find('_User', { 'username': 'zxcv' }, { fields: {} }, { limit: 1 }) + .then(results => { // _perishable_token should be unset after reset password expect(results.length).toEqual(1); expect(results[0]['_perishable_token']).toEqual(undefined); From 48145ab82ede8586e8b96907ed9f9f0b2c0b31a9 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 12:27:52 -0700 Subject: [PATCH 11/44] make adaptiveCollection private --- .../Storage/Mongo/MongoStorageAdapter.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 622baf6593..553763a522 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -75,7 +75,7 @@ export class MongoStorageAdapter { }); } - adaptiveCollection(name: string) { + _adaptiveCollection(name: string) { return this.connect() .then(() => this.database.collection(this._collectionPrefix + name)) .then(rawCollection => new MongoCollection(rawCollection)); @@ -83,7 +83,7 @@ export class MongoStorageAdapter { schemaCollection() { return this.connect() - .then(() => this.adaptiveCollection(MongoSchemaCollectionName)) + .then(() => this._adaptiveCollection(MongoSchemaCollectionName)) .then(collection => new MongoSchemaCollection(collection)); } @@ -148,7 +148,7 @@ export class MongoStorageAdapter { schemaUpdate['$unset'][name] = null; }); - return this.adaptiveCollection(className) + return this._adaptiveCollection(className) .then(collection => collection.updateMany({}, collectionUpdate)) .then(updateResult => this.schemaCollection()) .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate)); @@ -174,7 +174,7 @@ export class MongoStorageAdapter { // the schem only for the legacy mongo format. We'll figure that out later. createObject(className, object, schema) { const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema); - return this.adaptiveCollection(className) + return this._adaptiveCollection(className) .then(collection => collection.insertOne(mongoObject)) .catch(error => { if (error.code === 11000) { // Duplicate value @@ -189,7 +189,7 @@ export class MongoStorageAdapter { // If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined. // If there is some other error, reject with INTERNAL_SERVER_ERROR. deleteObjectsByQuery(className, query, schema) { - return this.adaptiveCollection(className) + return this._adaptiveCollection(className) .then(collection => { let mongoWhere = transformWhere(className, query, schema); return collection.deleteMany(mongoWhere) @@ -208,7 +208,7 @@ export class MongoStorageAdapter { updateObjectsByQuery(className, query, schema, update) { const mongoUpdate = transformUpdate(className, update, schema); const mongoWhere = transformWhere(className, query, schema); - return this.adaptiveCollection(className) + return this._adaptiveCollection(className) .then(collection => collection.updateMany(mongoWhere, mongoUpdate)); } @@ -217,7 +217,7 @@ export class MongoStorageAdapter { findOneAndUpdate(className, query, schema, update) { const mongoUpdate = transformUpdate(className, update, schema); const mongoWhere = transformWhere(className, query, schema); - return this.adaptiveCollection(className) + return this._adaptiveCollection(className) .then(collection => collection._mongoCollection.findAndModify(mongoWhere, [], mongoUpdate, { new: true })) .then(result => result.value); } @@ -226,7 +226,7 @@ export class MongoStorageAdapter { upsertOneObject(className, query, schema, update) { const mongoUpdate = transformUpdate(className, update, schema); const mongoWhere = transformWhere(className, query, schema); - return this.adaptiveCollection(className) + return this._adaptiveCollection(className) .then(collection => collection.upsertOne(mongoWhere, mongoUpdate)); } @@ -234,7 +234,7 @@ export class MongoStorageAdapter { find(className, query, schema, { skip, limit, sort }) { let mongoWhere = transformWhere(className, query, schema); let mongoSort = _.mapKeys(sort, (value, fieldName) => transformKey(className, fieldName, schema)); - return this.adaptiveCollection(className) + return this._adaptiveCollection(className) .then(collection => collection.find(mongoWhere, { skip, limit, sort: mongoSort })) .then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema))); } @@ -250,7 +250,7 @@ export class MongoStorageAdapter { mongoFieldNames.forEach(fieldName => { indexCreationRequest[fieldName] = 1; }); - return this.adaptiveCollection(className) + return this._adaptiveCollection(className) .then(collection => collection._ensureSparseUniqueIndexInBackground(indexCreationRequest)) .catch(error => { if (error.code === 11000) { @@ -263,12 +263,12 @@ export class MongoStorageAdapter { // Used in tests _rawFind(className, query) { - return this.adaptiveCollection(className).then(collection => collection.find(query)); + return this._adaptiveCollection(className).then(collection => collection.find(query)); } // Executs a count. count(className, query, schema) { - return this.adaptiveCollection(className) + return this._adaptiveCollection(className) .then(collection => collection.count(transformWhere(className, query, schema))); } } From aa5036327d5be41d06a9bfae43df33649fb49c66 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 12:38:29 -0700 Subject: [PATCH 12/44] Remove collection from mongoadapter --- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 553763a522..bc0010c815 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -69,12 +69,6 @@ export class MongoStorageAdapter { return this.connectionPromise; } - collection(name: string) { - return this.connect().then(() => { - return this.database.collection(name); - }); - } - _adaptiveCollection(name: string) { return this.connect() .then(() => this.database.collection(this._collectionPrefix + name)) @@ -99,7 +93,8 @@ export class MongoStorageAdapter { // exist, resolve with undefined. If schema exists, but can't be deleted for some other reason, // reject with INTERNAL_SERVER_ERROR. deleteOneSchema(className: string) { - return this.collection(this._collectionPrefix + className).then(collection => collection.drop()) + return this._adaptiveCollection(className) + .then(collection => collection.drop()) .catch(error => { // 'ns not found' means collection was already gone. Ignore deletion attempt. if (error.message == 'ns not found') { From 1cb89d3d7707d593e01d684371a7d7a1f94bfc8e Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 13:21:34 -0700 Subject: [PATCH 13/44] Move schema collection usage into mongo adapter --- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 5 ++++- src/Controllers/DatabaseController.js | 15 +++++---------- src/Routers/SchemasRouter.js | 13 +++++-------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index bc0010c815..e28d46591d 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -101,7 +101,10 @@ export class MongoStorageAdapter { return; } throw error; - }); + }) + // We've dropped the collection, now remove the _SCHEMA document + .then(() => this.schemaCollection()) + .then(schemaCollection => schemaCollection.findAndDeleteSchema(className)) } // Delete all data known to this adatper. Used for testing. diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 8801d12ccb..f556f09c05 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -728,17 +728,12 @@ const untransformObjectACL = ({_rperm, _wperm, ...output}) => { DatabaseController.prototype.deleteSchema = function(className) { return this.collectionExists(className) - .then(exist => { - if (!exist) { - return Promise.resolve(); + .then(exist => this.adapter.count(className)) + .then(count => { + if (count > 0) { + throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`); } - return this.adapter.count(className) - .then(count => { - if (count > 0) { - throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`); - } - return this.adapter.deleteOneSchema(className); - }) + return this.adapter.deleteOneSchema(className); }); } diff --git a/src/Routers/SchemasRouter.js b/src/Routers/SchemasRouter.js index da8307c7ea..c1b48bc297 100644 --- a/src/Routers/SchemasRouter.js +++ b/src/Routers/SchemasRouter.js @@ -75,21 +75,18 @@ var removeJoinTables = (database, mongoSchema) => { ); }; -function deleteSchema(req) { +const deleteSchema = req => { if (!SchemaController.classNameIsValid(req.params.className)) { throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, SchemaController.invalidClassNameMessage(req.params.className)); } return req.config.database.deleteSchema(req.params.className) - .then(() => req.config.database.schemaCollection()) - // We've dropped the collection now, so delete the item from _SCHEMA - // and clear the _Join collections - .then(coll => coll.findAndDeleteSchema(req.params.className)) - .then(document => { - if (document === null) { + .then(doc => { + if (!doc) { //tried to delete non-existent class return Promise.resolve(); } - return removeJoinTables(req.config.database, document); + // clear the _Join collections + return removeJoinTables(req.config.database, doc); }) .then(() => ({ response: {} })); } From 177b68ab91c6f132c187429712806632f0408f1a Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 15:13:14 -0700 Subject: [PATCH 14/44] stop relying on mongo format for removing join tables --- .../Storage/Mongo/MongoSchemaCollection.js | 8 +---- .../Storage/Mongo/MongoStorageAdapter.js | 5 ++- src/Controllers/DatabaseController.js | 33 +++++++++++++++---- src/Routers/SchemasRouter.js | 19 ----------- 4 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js index 8a0c2c12b6..4a171ba78e 100644 --- a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js +++ b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js @@ -154,14 +154,8 @@ class MongoSchemaCollection { } // Atomically find and delete an object based on query. - // The result is the promise with an object that was in the database before deleting. - // Postgres Note: Translates directly to `DELETE * FROM ... RETURNING *`, which will return data after delete is done. findAndDeleteSchema(name: string) { - // arguments: query, sort - return this._collection._mongoCollection.findAndRemove(_mongoSchemaQueryFromNameQuery(name), []).then(document => { - // Value is the object where mongo returns multiple fields. - return document.value; - }); + return this._collection._mongoCollection.findAndRemove(_mongoSchemaQueryFromNameQuery(name), []); } // Add a collection. Currently the input is in mongo format, but that will change to Parse format in a diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index e28d46591d..311115d23e 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -89,9 +89,8 @@ export class MongoStorageAdapter { }); } - // Deletes a schema. Resolve if successful. If the schema doesn't - // exist, resolve with undefined. If schema exists, but can't be deleted for some other reason, - // reject with INTERNAL_SERVER_ERROR. + // Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.) + // and resolves with false if it wasn't (eg. a join table). Rejects if deletion was impossible. deleteOneSchema(className: string) { return this._adaptiveCollection(className) .then(collection => collection.drop()) diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index f556f09c05..694b9575b0 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -727,14 +727,33 @@ const untransformObjectACL = ({_rperm, _wperm, ...output}) => { } DatabaseController.prototype.deleteSchema = function(className) { - return this.collectionExists(className) - .then(exist => this.adapter.count(className)) - .then(count => { - if (count > 0) { - throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`); + return this.loadSchema() + .then(schemaController => schemaController.getOneSchema(className)) + .catch(error => { + if (error === undefined) { + return { fields: {} }; + } else { + throw error; } - return this.adapter.deleteOneSchema(className); - }); + }) + .then(schema => { + return this.collectionExists(className) + .then(exist => this.adapter.count(className)) + .then(count => { + if (count > 0) { + throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`); + } + return this.adapter.deleteOneSchema(className); + }) + .then(wasParseCollection => { + if (wasParseCollection) { + const relationFieldNames = Object.keys(schema.fields).filter(fieldName => schema.fields[fieldName].type === 'Relation'); + return Promise.all(relationFieldNames.map(name => this.adapter.deleteOneSchema(joinTableName(className, name)))); + } else { + return Promise.resolve(); + } + }); + }) } DatabaseController.prototype.addPointerPermissions = function(schema, className, operation, query, aclGroup = []) { diff --git a/src/Routers/SchemasRouter.js b/src/Routers/SchemasRouter.js index c1b48bc297..ce54662693 100644 --- a/src/Routers/SchemasRouter.js +++ b/src/Routers/SchemasRouter.js @@ -64,30 +64,11 @@ function modifySchema(req) { .then(result => ({response: result})); } -// A helper function that removes all join tables for a schema. Returns a promise. -var removeJoinTables = (database, mongoSchema) => { - return Promise.all(Object.keys(mongoSchema) - .filter(field => field !== '_metadata' && mongoSchema[field].startsWith('relation<')) - .map(field => { - let collectionName = `_Join:${field}:${mongoSchema._id}`; - return database.adapter.deleteOneSchema(collectionName); - }) - ); -}; - const deleteSchema = req => { if (!SchemaController.classNameIsValid(req.params.className)) { throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, SchemaController.invalidClassNameMessage(req.params.className)); } return req.config.database.deleteSchema(req.params.className) - .then(doc => { - if (!doc) { - //tried to delete non-existent class - return Promise.resolve(); - } - // clear the _Join collections - return removeJoinTables(req.config.database, doc); - }) .then(() => ({ response: {} })); } From 4297a8de7bfdc12566d71d7cf9b43ab37a83b8a0 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 15:19:46 -0700 Subject: [PATCH 15/44] reduce usage of schemaCollection --- spec/schemas.spec.js | 6 +++--- src/Controllers/DatabaseController.js | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 5f25817dd6..ffecf3c265 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -327,9 +327,9 @@ describe('schemas', () => { }); it('responds with all fields when getting incomplete schema', done => { - config.database.schemaCollection().then((schema) => { - return schema.addSchema('_User'); - }).then(() => { + config.database.loadSchema() + .then(schemaController => schemaController.addClassIfNotExists('_User', {}, defaultClassLevelPermissions)) + .then(() => { request.get({ url: 'http://localhost:8378/1/schemas/_User', headers: masterKeyHeaders, diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 694b9575b0..c74361da64 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -109,9 +109,8 @@ DatabaseController.prototype.validateClassName = function(className) { // If we are provided a acceptor, then we run it on the schema. // If the schema isn't accepted, we reload it at most once. DatabaseController.prototype.loadSchema = function() { - if (!this.schemaPromise) { - this.schemaPromise = this.schemaCollection().then(collection => { + this.schemaPromise = this.adapter.schemaCollection().then(collection => { delete this.schemaPromise; return SchemaController.load(collection, this.adapter); }); From f1fa13d522a9c1ad7b66e07e10f18b1a954801b1 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 15:50:24 -0700 Subject: [PATCH 16/44] remove uses of _collection --- spec/schemas.spec.js | 2 +- .../Storage/Mongo/MongoSchemaCollection.js | 15 +++++++-------- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 10 ++++++++++ src/Controllers/SchemaController.js | 8 ++++---- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index ffecf3c265..6919cf39a9 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -100,7 +100,7 @@ var masterKeyHeaders = { 'X-Parse-Master-Key': 'test', }; -describe('schemas', () => { +fdescribe('schemas', () => { it('requires the master key to get all schemas', (done) => { request.get({ url: 'http://localhost:8378/1/schemas', diff --git a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js index 4a171ba78e..0d96d8fbba 100644 --- a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js +++ b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js @@ -158,10 +158,8 @@ class MongoSchemaCollection { return this._collection._mongoCollection.findAndRemove(_mongoSchemaQueryFromNameQuery(name), []); } - // Add a collection. Currently the input is in mongo format, but that will change to Parse format in a - // later PR. Returns a promise that is expected to resolve with the newly created schema, in Parse format. - // If the class already exists, returns a promise that rejects with undefined as the reason. If the collection - // can't be added for a reason other than it already existing, requirements for rejection reason are TBD. + // Returns a promise that is expected to resolve with the newly created schema, in Parse format. + // If the class already exists, returns a promise that rejects with DUPLICATE_VALUE as the reason. addSchema(name: string, fields, classLevelPermissions) { let mongoSchema = mongoSchemaFromFieldsAndClassNameAndCLP(fields, name, classLevelPermissions); let mongoObject = _mongoSchemaObjectFromNameFields(name, mongoSchema); @@ -169,9 +167,10 @@ class MongoSchemaCollection { .then(result => mongoSchemaToParseSchema(result.ops[0])) .catch(error => { if (error.code === 11000) { //Mongo's duplicate key error - throw undefined; + throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Class already exists.'); + } else { + throw error; } - throw error; }); } @@ -186,8 +185,8 @@ class MongoSchemaCollection { // Add a field to the schema. If database does not support the field // type (e.g. mongo doesn't support more than one GeoPoint in a class) reject with an "Incorrect Type" // Parse error with a desciptive message. If the field already exists, this function must - // not modify the schema, and must reject with an error. Exact error format is TBD. If this function - // is called for a class that doesn't exist, this function must create that class. + // not modify the schema, and must reject with DUPLICATE_VALUE error. + // If this is called for a class that doesn't exist, this function must create that class. // TODO: throw an error if an unsupported field type is passed. Deciding whether a type is supported // should be the job of the adapter. Some adapters may not support GeoPoint at all. Others may diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 311115d23e..0b2a825443 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -89,6 +89,16 @@ export class MongoStorageAdapter { }); } + createCollection(className, fields, classLevelPermissions) { + return this.schemaCollection() + .then(schemaCollection => schemaCollection.addSchema(className, fields, classLevelPermissions)); + } + + addFieldIfNotExists(className, fieldName, type) { + return this.schemaCollection() + .then(schemaCollection => schemaCollection.addFieldIfNotExists(className, fieldName, type)); + } + // Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.) // and resolves with false if it wasn't (eg. a join table). Rejects if deletion was impossible. deleteOneSchema(className: string) { diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index 49ec0dde9d..cab06d3f50 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -295,12 +295,12 @@ class SchemaController { return Promise.reject(validationError); } - return this._collection.addSchema(className, fields, classLevelPermissions) + return this._dbAdapter.createCollection(className, fields, classLevelPermissions) .catch(error => { - if (error === undefined) { + if (error && error.code === Parse.Error.DUPLICATE_VALUE) { throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`); } else { - throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error.'); + throw error; } }); } @@ -511,7 +511,7 @@ class SchemaController { type = { type }; } - return this._collection.addFieldIfNotExists(className, fieldName, type).then(() => { + return this._dbAdapter.addFieldIfNotExists(className, fieldName, type).then(() => { // The update succeeded. Reload the schema return this.reloadData(); }, () => { From 55289286069c74a7c2c61e301ffd3c23ece8afdf Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 16:00:05 -0700 Subject: [PATCH 17/44] Move CLP setting into mongo adapter --- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 7 +++++++ src/Controllers/SchemaController.js | 12 ++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 0b2a825443..faafd3cbbd 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -89,6 +89,13 @@ export class MongoStorageAdapter { }); } + setClassLevelPermissions(className, CLPs) { + return this.schemaCollection() + .then(schemaCollection => schemaCollection.updateSchema(className, { + $set: { _metadata: { class_permissions: CLPs } } + })); + } + createCollection(className, fields, classLevelPermissions) { return this.schemaCollection() .then(schemaCollection => schemaCollection.addSchema(className, fields, classLevelPermissions)); diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index cab06d3f50..6b0a730e10 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -452,16 +452,8 @@ class SchemaController { return Promise.resolve(); } validateCLP(perms, newSchema); - let update = { - _metadata: { - class_permissions: perms - } - }; - update = {'$set': update}; - return this._collection.updateSchema(className, update).then(() => { - // The update succeeded. Reload the schema - return this.reloadData(); - }); + return this._dbAdapter.setClassLevelPermissions(className, perms) + .then(() => this.reloadData()); } // Returns a promise that resolves successfully to the new schema From 990b08e7d3788a3a6d0257553eb81b9928d8e1fd Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 16:06:58 -0700 Subject: [PATCH 18/44] remove all uses of schemaCollection --- spec/schemas.spec.js | 2 +- src/Controllers/DatabaseController.js | 10 +++------- src/Controllers/SchemaController.js | 8 +++----- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 6919cf39a9..ffecf3c265 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -100,7 +100,7 @@ var masterKeyHeaders = { 'X-Parse-Master-Key': 'test', }; -fdescribe('schemas', () => { +describe('schemas', () => { it('requires the master key to get all schemas', (done) => { request.get({ url: 'http://localhost:8378/1/schemas', diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index c74361da64..8363adae84 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -105,15 +105,11 @@ DatabaseController.prototype.validateClassName = function(className) { return Promise.resolve(); }; -// Returns a promise for a schema object. -// If we are provided a acceptor, then we run it on the schema. -// If the schema isn't accepted, we reload it at most once. +// Returns a promise for a schemaController. DatabaseController.prototype.loadSchema = function() { if (!this.schemaPromise) { - this.schemaPromise = this.adapter.schemaCollection().then(collection => { - delete this.schemaPromise; - return SchemaController.load(collection, this.adapter); - }); + this.schemaPromise = SchemaController.load(this.adapter); + this.schemaPromise.then(() => delete this.schemaPromise); } return this.schemaPromise; }; diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index 6b0a730e10..f334e9544b 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -233,13 +233,11 @@ const injectDefaultSchema = schema => ({ // Stores the entire schema of the app in a weird hybrid format somewhere between // the mongo format and the Parse format. Soon, this will all be Parse format. class SchemaController { - _collection; _dbAdapter; data; perms; - constructor(collection, databaseAdapter) { - this._collection = collection; + constructor(databaseAdapter) { this._dbAdapter = databaseAdapter; // this.data[className][fieldName] tells you the type of that field, in mongo format @@ -688,8 +686,8 @@ class SchemaController { } // Returns a promise for a new Schema. -function load(collection, dbAdapter) { - let schema = new SchemaController(collection, dbAdapter); +const load = dbAdapter => { + let schema = new SchemaController(dbAdapter); return schema.reloadData().then(() => schema); } From 74eb5c07911fa185fc877812e9f3b0e781aa6cdd Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 16:09:23 -0700 Subject: [PATCH 19/44] make schemaCollection private --- .../Storage/Mongo/MongoStorageAdapter.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index faafd3cbbd..7875896e18 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -75,7 +75,7 @@ export class MongoStorageAdapter { .then(rawCollection => new MongoCollection(rawCollection)); } - schemaCollection() { + _schemaCollection() { return this.connect() .then(() => this._adaptiveCollection(MongoSchemaCollectionName)) .then(collection => new MongoSchemaCollection(collection)); @@ -90,19 +90,19 @@ export class MongoStorageAdapter { } setClassLevelPermissions(className, CLPs) { - return this.schemaCollection() + return this._schemaCollection() .then(schemaCollection => schemaCollection.updateSchema(className, { $set: { _metadata: { class_permissions: CLPs } } })); } createCollection(className, fields, classLevelPermissions) { - return this.schemaCollection() + return this._schemaCollection() .then(schemaCollection => schemaCollection.addSchema(className, fields, classLevelPermissions)); } addFieldIfNotExists(className, fieldName, type) { - return this.schemaCollection() + return this._schemaCollection() .then(schemaCollection => schemaCollection.addFieldIfNotExists(className, fieldName, type)); } @@ -119,7 +119,7 @@ export class MongoStorageAdapter { throw error; }) // We've dropped the collection, now remove the _SCHEMA document - .then(() => this.schemaCollection()) + .then(() => this._schemaCollection()) .then(schemaCollection => schemaCollection.findAndDeleteSchema(className)) } @@ -164,7 +164,7 @@ export class MongoStorageAdapter { return this._adaptiveCollection(className) .then(collection => collection.updateMany({}, collectionUpdate)) - .then(updateResult => this.schemaCollection()) + .then(updateResult => this._schemaCollection()) .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate)); } @@ -172,14 +172,14 @@ export class MongoStorageAdapter { // schemas cannot be retrieved, returns a promise that rejects. Requirements for the // rejection reason are TBD. getAllSchemas() { - return this.schemaCollection().then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA()); + return this._schemaCollection().then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA()); } // Return a promise for the schema with the given name, in Parse format. If // this adapter doesn't know about the schema, return a promise that rejects with // undefined as the reason. getOneSchema(className) { - return this.schemaCollection() + return this._schemaCollection() .then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className)); } From c5e7493cd53e296d2b391e82824fbf0c89e66174 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 16:14:20 -0700 Subject: [PATCH 20/44] remove transform from schemaCollection --- src/Adapters/Storage/Mongo/MongoSchemaCollection.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js index 0d96d8fbba..0b7394ac34 100644 --- a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js +++ b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js @@ -1,6 +1,5 @@ import MongoCollection from './MongoCollection'; -import * as transform from './MongoTransform'; function mongoFieldToParseSchemaField(type) { if (type[0] === '*') { @@ -222,10 +221,6 @@ class MongoSchemaCollection { ); }); } - - get transform() { - return transform; - } } // Exported for testing reasons and because we haven't moved all mongo schema format From 92604c26e3c203796a5a11a0c9def3faa49bd10d Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 17:00:51 -0700 Subject: [PATCH 21/44] rename some stuff --- spec/MongoStorageAdapter.spec.js | 2 +- spec/helper.js | 2 +- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 12 ++++++------ src/Controllers/DatabaseController.js | 8 ++++---- src/Controllers/SchemaController.js | 12 ++++++------ src/Routers/SchemasRouter.js | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index 069e0166bf..968b1e4646 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -9,7 +9,7 @@ const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDataba describe('MongoStorageAdapter', () => { beforeEach(done => { new MongoStorageAdapter({ uri: databaseURI }) - .deleteAllSchemas() + .deleteAllClasses() .then(done, fail); }); diff --git a/spec/helper.js b/spec/helper.js index 81cfdd06ef..273d0d4935 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -131,7 +131,7 @@ beforeEach(done => { afterEach(function(done) { Parse.Cloud._removeAllHooks(); - mongoAdapter.getAllSchemas() + mongoAdapter.getAllClasses() .then(allSchemas => { allSchemas.forEach((schema) => { var className = schema.className; diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 7875896e18..ab8fc3493c 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -81,7 +81,7 @@ export class MongoStorageAdapter { .then(collection => new MongoSchemaCollection(collection)); } - collectionExists(name: string) { + classExists(name: string) { return this.connect().then(() => { return this.database.listCollections({ name: this._collectionPrefix + name }).toArray(); }).then(collections => { @@ -96,7 +96,7 @@ export class MongoStorageAdapter { })); } - createCollection(className, fields, classLevelPermissions) { + createClass(className, fields, classLevelPermissions) { return this._schemaCollection() .then(schemaCollection => schemaCollection.addSchema(className, fields, classLevelPermissions)); } @@ -108,7 +108,7 @@ export class MongoStorageAdapter { // Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.) // and resolves with false if it wasn't (eg. a join table). Rejects if deletion was impossible. - deleteOneSchema(className: string) { + deleteClass(className: string) { return this._adaptiveCollection(className) .then(collection => collection.drop()) .catch(error => { @@ -124,7 +124,7 @@ export class MongoStorageAdapter { } // Delete all data known to this adatper. Used for testing. - deleteAllSchemas() { + deleteAllClasses() { return storageAdapterAllCollections(this) .then(collections => Promise.all(collections.map(collection => collection.drop()))); } @@ -171,14 +171,14 @@ export class MongoStorageAdapter { // Return a promise for all schemas known to this adapter, in Parse format. In case the // schemas cannot be retrieved, returns a promise that rejects. Requirements for the // rejection reason are TBD. - getAllSchemas() { + getAllClasses() { return this._schemaCollection().then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA()); } // Return a promise for the schema with the given name, in Parse format. If // this adapter doesn't know about the schema, return a promise that rejects with // undefined as the reason. - getOneSchema(className) { + getClass(className) { return this._schemaCollection() .then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className)); } diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 8363adae84..307efc7c9f 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -92,7 +92,7 @@ DatabaseController.prototype.schemaCollection = function() { }; DatabaseController.prototype.collectionExists = function(className) { - return this.adapter.collectionExists(className); + return this.adapter.classExists(className); }; DatabaseController.prototype.validateClassName = function(className) { @@ -429,7 +429,7 @@ DatabaseController.prototype.canAddField = function(schema, className, object, a // Returns a promise. DatabaseController.prototype.deleteEverything = function() { this.schemaPromise = null; - return this.adapter.deleteAllSchemas(); + return this.adapter.deleteAllClasses(); }; // Finds the keys in a query. Returns a Set. REST format only @@ -738,12 +738,12 @@ DatabaseController.prototype.deleteSchema = function(className) { if (count > 0) { throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`); } - return this.adapter.deleteOneSchema(className); + return this.adapter.deleteClass(className); }) .then(wasParseCollection => { if (wasParseCollection) { const relationFieldNames = Object.keys(schema.fields).filter(fieldName => schema.fields[fieldName].type === 'Relation'); - return Promise.all(relationFieldNames.map(name => this.adapter.deleteOneSchema(joinTableName(className, name)))); + return Promise.all(relationFieldNames.map(name => this.adapter.deleteClass(joinTableName(className, name)))); } else { return Promise.resolve(); } diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index f334e9544b..ae655776bc 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -249,7 +249,7 @@ class SchemaController { reloadData() { this.data = {}; this.perms = {}; - return this.getAllSchemas() + return this.getAllClasses() .then(allSchemas => { allSchemas.forEach(schema => { this.data[schema.className] = schema.fields; @@ -267,8 +267,8 @@ class SchemaController { }); } - getAllSchemas() { - return this._dbAdapter.getAllSchemas() + getAllClasses() { + return this._dbAdapter.getAllClasses() .then(allSchemas => allSchemas.map(injectDefaultSchema)); } @@ -276,7 +276,7 @@ class SchemaController { if (allowVolatileClasses && volatileClasses.indexOf(className) > -1) { return Promise.resolve(this.data[className]); } - return this._dbAdapter.getOneSchema(className) + return this._dbAdapter.getClass(className) .then(injectDefaultSchema); } @@ -293,7 +293,7 @@ class SchemaController { return Promise.reject(validationError); } - return this._dbAdapter.createCollection(className, fields, classLevelPermissions) + return this._dbAdapter.createClass(className, fields, classLevelPermissions) .catch(error => { if (error && error.code === Parse.Error.DUPLICATE_VALUE) { throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`); @@ -552,7 +552,7 @@ class SchemaController { if (this.data[className][fieldName].type == 'Relation') { //For relations, drop the _Join table return database.adapter.deleteFields(className, [fieldName], []) - .then(() => database.adapter.deleteOneSchema(`_Join:${fieldName}:${className}`)); + .then(() => database.adapter.deleteClass(`_Join:${fieldName}:${className}`)); } const fieldNames = [fieldName]; diff --git a/src/Routers/SchemasRouter.js b/src/Routers/SchemasRouter.js index ce54662693..35b5d92dda 100644 --- a/src/Routers/SchemasRouter.js +++ b/src/Routers/SchemasRouter.js @@ -16,7 +16,7 @@ function classNameMismatchResponse(bodyClass, pathClass) { function getAllSchemas(req) { return req.config.database.loadSchema() - .then(schemaController => schemaController.getAllSchemas()) + .then(schemaController => schemaController.getAllClasses()) .then(schemas => ({ response: { results: schemas } })); } From f03bb3e551c15e296281644e9aac09299c0a2aa8 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 17:26:58 -0700 Subject: [PATCH 22/44] Tweak paramaters and stuff --- .../Storage/Mongo/MongoStorageAdapter.js | 21 ++++++++++++------- src/Controllers/SchemaController.js | 12 +++++------ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index ab8fc3493c..200276656b 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -81,7 +81,7 @@ export class MongoStorageAdapter { .then(collection => new MongoSchemaCollection(collection)); } - classExists(name: string) { + classExists(name) { return this.connect().then(() => { return this.database.listCollections({ name: this._collectionPrefix + name }).toArray(); }).then(collections => { @@ -96,9 +96,9 @@ export class MongoStorageAdapter { })); } - createClass(className, fields, classLevelPermissions) { + createClass(className, schema) { return this._schemaCollection() - .then(schemaCollection => schemaCollection.addSchema(className, fields, classLevelPermissions)); + .then(schemaCollection => schemaCollection.addSchema(className, schema.fields, schema.classLevelPermissions)); } addFieldIfNotExists(className, fieldName, type) { @@ -108,7 +108,7 @@ export class MongoStorageAdapter { // Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.) // and resolves with false if it wasn't (eg. a join table). Rejects if deletion was impossible. - deleteClass(className: string) { + deleteClass(className) { return this._adaptiveCollection(className) .then(collection => collection.drop()) .catch(error => { @@ -149,9 +149,14 @@ export class MongoStorageAdapter { // may do so. // Returns a Promise. - deleteFields(className: string, fieldNames, pointerFieldNames) { - const nonPointerFieldNames = _.difference(fieldNames, pointerFieldNames); - const mongoFormatNames = nonPointerFieldNames.concat(pointerFieldNames.map(name => `_p_${name}`)); + deleteFields(className, schema, fieldNames) { + const mongoFormatNames = fieldNames.map(fieldName => { + if (schema.fields[fieldName].type === 'Pointer') { + return `_p_${fieldName}` + } else { + return fieldName; + } + }); const collectionUpdate = { '$unset' : {} }; mongoFormatNames.forEach(name => { collectionUpdate['$unset'][name] = null; @@ -164,7 +169,7 @@ export class MongoStorageAdapter { return this._adaptiveCollection(className) .then(collection => collection.updateMany({}, collectionUpdate)) - .then(updateResult => this._schemaCollection()) + .then(() => this._schemaCollection()) .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate)); } diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index ae655776bc..649dc66a46 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -293,7 +293,7 @@ class SchemaController { return Promise.reject(validationError); } - return this._dbAdapter.createClass(className, fields, classLevelPermissions) + return this._dbAdapter.createClass(className, { fields, classLevelPermissions }) .catch(error => { if (error && error.code === Parse.Error.DUPLICATE_VALUE) { throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`); @@ -548,16 +548,16 @@ class SchemaController { if (!this.data[className][fieldName]) { throw new Parse.Error(255, `Field ${fieldName} does not exist, cannot delete.`); } - + }) + .then(() => this.getOneSchema(className)) + .then(schema => { if (this.data[className][fieldName].type == 'Relation') { //For relations, drop the _Join table - return database.adapter.deleteFields(className, [fieldName], []) + return database.adapter.deleteFields(className, schema, [fieldName]) .then(() => database.adapter.deleteClass(`_Join:${fieldName}:${className}`)); } - const fieldNames = [fieldName]; - const pointerFieldNames = this.data[className][fieldName].type === 'Pointer' ? [fieldName] : []; - return database.adapter.deleteFields(className, fieldNames, pointerFieldNames); + return database.adapter.deleteFields(className, schema, [fieldName]); }); } From 019cf0718af43f48f6c2df78b42846d74a769ac7 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 17:43:24 -0700 Subject: [PATCH 23/44] reorder some params --- spec/MongoStorageAdapter.spec.js | 12 ++++++------ spec/ParseAPI.spec.js | 19 +++++++++++++++++++ spec/ParseGlobalConfig.spec.js | 6 +++--- .../Storage/Mongo/MongoStorageAdapter.js | 10 +++++----- src/Controllers/DatabaseController.js | 14 +++++++------- 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index 968b1e4646..cef8a8552e 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -49,7 +49,7 @@ describe('MongoStorageAdapter', () => { it('stores objectId in _id', done => { let adapter = new MongoStorageAdapter({ uri: databaseURI }); - adapter.createObject('Foo', { objectId: 'abcde' }, { fields: { objectId: 'String' } }) + adapter.createObject('Foo', {}, { objectId: 'abcde' }) .then(() => adapter._rawFind('Foo', {})) .then(results => { expect(results.length).toEqual(1); @@ -70,10 +70,10 @@ describe('MongoStorageAdapter', () => { } }; let adapter = new MongoStorageAdapter({ uri: databaseURI }); - adapter.createObject('APointerDarkly', obj, { fields: { + adapter.createObject('APointerDarkly', { fields: { objectId: { type: 'String' }, aPointer: { type: 'Pointer', targetClass: 'JustThePointer' }, - }}) + }}, obj) .then(() => adapter._rawFind('APointerDarkly', {})) .then(results => { expect(results.length).toEqual(1); @@ -90,7 +90,7 @@ describe('MongoStorageAdapter', () => { let adapter = new MongoStorageAdapter({ uri: databaseURI }); let schema = { fields : { subdoc: { type: 'Object' } } }; let obj = { subdoc: {foo: 'bar', wu: 'tan'} }; - adapter.createObject('MyClass', obj, schema) + adapter.createObject('MyClass', schema, obj) .then(() => adapter._rawFind('MyClass', {})) .then(results => { expect(results.length).toEqual(1); @@ -99,7 +99,7 @@ describe('MongoStorageAdapter', () => { expect(mob.subdoc.foo).toBe('bar'); expect(mob.subdoc.wu).toBe('tan'); let obj = { 'subdoc.wu': 'clan' }; - return adapter.findOneAndUpdate('MyClass', {}, schema, obj); + return adapter.findOneAndUpdate('MyClass', schema, {}, obj); }) .then(() => adapter._rawFind('MyClass', {})) .then(results => { @@ -127,7 +127,7 @@ describe('MongoStorageAdapter', () => { object: { type: 'Object' }, date: { type: 'Date' }, } }; - adapter.createObject('MyClass', obj, schema) + adapter.createObject('MyClass', schema, obj) .then(() => adapter._rawFind('MyClass', {})) .then(results => { expect(results.length).toEqual(1); diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index dfe60d5b3e..40c99c1f85 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -127,6 +127,15 @@ describe('miscellaneous', function() { it('ensure that if people already have duplicate users, they can still sign up new users', done => { // Remove existing data to clear out unique index TestUtils.destroyAllDataPermanently() + let config = new Config('test'); + .then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'x', username: 'u' })) + .then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'y', username: 'u' })) + .then(() => { + let user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('zxcv'); + return user.signUp(); + }) .then(() => { let adapter = new MongoStorageAdapter({ uri: 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase', @@ -163,6 +172,16 @@ describe('miscellaneous', function() { it('ensure that if people already have duplicate emails, they can still sign up new users', done => { // Wipe out existing database with unique index so we can create a duplicate user TestUtils.destroyAllDataPermanently() + .then(TestUtils.destroyAllDataPermanently) + .then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'x', email: 'a@b.c' })) + .then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'y', email: 'a@b.c' })) + .then(() => { + let user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('qqq'); + user.setEmail('unique@unique.unique'); + return user.signUp(); + }) .then(() => { let config = new Config('test'); config.database.adapter.createObject('_User', { objectId: 'x', email: 'a@b.c' }, requiredUserFields) diff --git a/spec/ParseGlobalConfig.spec.js b/spec/ParseGlobalConfig.spec.js index c1d23b5623..504e26990d 100644 --- a/spec/ParseGlobalConfig.spec.js +++ b/spec/ParseGlobalConfig.spec.js @@ -9,8 +9,8 @@ describe('a GlobalConfig', () => { let config = new Config('test'); config.database.adapter.upsertOneObject( '_GlobalConfig', + { fields: {} }, { objectId: 1 }, - { fields: { params: { __type: 'String' } } }, { params: { companies: ['US', 'DK'] } } ).then(done); }); @@ -95,8 +95,8 @@ describe('a GlobalConfig', () => { let config = new Config('test'); config.database.adapter.deleteObjectsByQuery( '_GlobalConfig', - { objectId: 1 }, - { fields: { params: { __type: 'String' } } } + { fields: { params: { __type: 'String' } } }, + { objectId: 1 } ).then(() => { request.get({ url : 'http://localhost:8378/1/config', diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 200276656b..9858dcb6ec 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -191,7 +191,7 @@ export class MongoStorageAdapter { // TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema, // and should infer from the type. Or maybe does need the schema for validations. Or maybe needs // the schem only for the legacy mongo format. We'll figure that out later. - createObject(className, object, schema) { + createObject(className, schema, object) { const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema); return this._adaptiveCollection(className) .then(collection => collection.insertOne(mongoObject)) @@ -207,7 +207,7 @@ export class MongoStorageAdapter { // Remove all objects that match the given Parse Query. // If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined. // If there is some other error, reject with INTERNAL_SERVER_ERROR. - deleteObjectsByQuery(className, query, schema) { + deleteObjectsByQuery(className, schema, query) { return this._adaptiveCollection(className) .then(collection => { let mongoWhere = transformWhere(className, query, schema); @@ -224,7 +224,7 @@ export class MongoStorageAdapter { } // Apply the update to all objects that match the given Parse Query. - updateObjectsByQuery(className, query, schema, update) { + updateObjectsByQuery(className, schema, query, update) { const mongoUpdate = transformUpdate(className, update, schema); const mongoWhere = transformWhere(className, query, schema); return this._adaptiveCollection(className) @@ -233,7 +233,7 @@ export class MongoStorageAdapter { // Atomically finds and updates an object based on query. // Resolve with the updated object. - findOneAndUpdate(className, query, schema, update) { + findOneAndUpdate(className, schema, query, update) { const mongoUpdate = transformUpdate(className, update, schema); const mongoWhere = transformWhere(className, query, schema); return this._adaptiveCollection(className) @@ -242,7 +242,7 @@ export class MongoStorageAdapter { } // Hopefully we can get rid of this. It's only used for config and hooks. - upsertOneObject(className, query, schema, update) { + upsertOneObject(className, schema, query, update) { const mongoUpdate = transformUpdate(className, update, schema); const mongoWhere = transformWhere(className, query, schema); return this._adaptiveCollection(className) diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 307efc7c9f..b43c34d3ad 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -227,11 +227,11 @@ DatabaseController.prototype.update = function(className, query, update, { } update = transformObjectACL(update); if (many) { - return this.adapter.updateObjectsByQuery(className, query, schema, update); + return this.adapter.updateObjectsByQuery(className, schema, query, update); } else if (upsert) { - return this.adapter.upsertOneObject(className, query, schema, update); + return this.adapter.upsertOneObject(className, schema, query, update); } else { - return this.adapter.findOneAndUpdate(className, query, schema, update); + return this.adapter.findOneAndUpdate(className, schema, query, update); } }); }) @@ -319,7 +319,7 @@ DatabaseController.prototype.addRelation = function(key, fromClassName, fromId, relatedId: toId, owningId : fromId }; - return this.adapter.upsertOneObject(`_Join:${key}:${fromClassName}`, doc, relationSchema, doc); + return this.adapter.upsertOneObject(`_Join:${key}:${fromClassName}`, relationSchema, doc, doc); }; // Removes a relation. @@ -330,7 +330,7 @@ DatabaseController.prototype.removeRelation = function(key, fromClassName, fromI relatedId: toId, owningId: fromId }; - return this.adapter.deleteObjectsByQuery(`_Join:${key}:${fromClassName}`, doc, relationSchema) + return this.adapter.deleteObjectsByQuery(`_Join:${key}:${fromClassName}`, relationSchema, doc) .catch(error => { // We don't care if they try to delete a non-existent relation. if (error.code == Parse.Error.OBJECT_NOT_FOUND) { @@ -375,7 +375,7 @@ DatabaseController.prototype.destroy = function(className, query, { acl } = {}) } throw error; }) - .then(parseFormatSchema => this.adapter.deleteObjectsByQuery(className, query, parseFormatSchema)) + .then(parseFormatSchema => this.adapter.deleteObjectsByQuery(className, parseFormatSchema, query)) .catch(error => { // When deleting sessions while changing passwords, don't throw an error if they don't have any sessions. if (className === "_Session" && error.code === Parse.Error.OBJECT_NOT_FOUND) { @@ -404,7 +404,7 @@ DatabaseController.prototype.create = function(className, object, { acl } = {}) .then(() => this.handleRelationUpdates(className, null, object)) .then(() => schemaController.enforceClassExists(className)) .then(() => schemaController.getOneSchema(className, true)) - .then(schema => this.adapter.createObject(className, object, schema)) + .then(schema => this.adapter.createObject(className, schema, object)) .then(result => sanitizeDatabaseResult(originalObject, result.ops[0])); }) }; From f2e16ee30050feaf69c0ab097e5259e936aafd93 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 18:26:31 -0700 Subject: [PATCH 24/44] reorder find() arguments --- spec/MongoStorageAdapter.spec.js | 2 +- spec/OAuth.spec.js | 4 +- spec/ParseInstallation.spec.js | 82 +++++++++---------- spec/RestCreate.spec.js | 14 ++-- spec/ValidationAndPasswordsReset.spec.js | 2 +- .../Storage/Mongo/MongoStorageAdapter.js | 2 +- src/Controllers/DatabaseController.js | 6 +- 7 files changed, 56 insertions(+), 56 deletions(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index cef8a8552e..2ead5c1ebc 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -135,7 +135,7 @@ describe('MongoStorageAdapter', () => { expect(mob.array instanceof Array).toBe(true); expect(typeof mob.object).toBe('object'); expect(mob.date instanceof Date).toBe(true); - return adapter.find('MyClass', {}, schema, {}); + return adapter.find('MyClass', schema, {}, {}); }) .then(results => { expect(results.length).toEqual(1); diff --git a/spec/OAuth.spec.js b/spec/OAuth.spec.js index ee505151ab..a8da7ed08b 100644 --- a/spec/OAuth.spec.js +++ b/spec/OAuth.spec.js @@ -284,9 +284,9 @@ describe('OAuth', function() { "Expiration should be cleared"); // make sure the auth data is properly deleted var config = new Config(Parse.applicationId); - config.database.adapter.find('_User', { objectId: model.id }, { + config.database.adapter.find('_User', { fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation), - }, {}) + }, { objectId: model.id }, {}) .then(res => { expect(res.length).toBe(1); expect(res[0]._auth_data_myoauth).toBeUndefined(); diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index 2bc191dacf..7663e9a48f 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -24,7 +24,7 @@ describe('Installations', () => { 'deviceType': device }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; @@ -42,7 +42,7 @@ describe('Installations', () => { 'deviceType': device }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; @@ -60,7 +60,7 @@ describe('Installations', () => { 'deviceType': device }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; @@ -79,7 +79,7 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; @@ -102,7 +102,7 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; @@ -200,7 +200,7 @@ describe('Installations', () => { 'custom': 'allowed' }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; @@ -224,7 +224,7 @@ describe('Installations', () => { var firstObject; var secondObject; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); firstObject = results[0]; @@ -233,7 +233,7 @@ describe('Installations', () => { input['foo'] = 'bar'; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); secondObject = results[0]; @@ -263,13 +263,13 @@ describe('Installations', () => { var firstObject; var secondObject; rest.create(config, auth.nobody(config), '_Installation', input1) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); firstObject = results[0]; return rest.create(config, auth.nobody(config), '_Installation', input2); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(2); if (results[0]['_id'] == firstObject._id) { @@ -279,7 +279,7 @@ describe('Installations', () => { } return rest.create(config, auth.nobody(config), '_Installation', input3); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0]['_id']).toEqual(secondObject._id); @@ -325,7 +325,7 @@ describe('Installations', () => { channels: ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var id = results[0].objectId; @@ -334,7 +334,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', id, update); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].channels.length).toEqual(1); @@ -356,7 +356,7 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { 'installationId': installId2 }; @@ -379,7 +379,7 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { 'deviceToken': b }; @@ -403,7 +403,7 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -413,7 +413,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].deviceToken).toEqual(u); @@ -429,7 +429,7 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -453,7 +453,7 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -461,7 +461,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0]['custom']).toEqual('allowed'); @@ -488,11 +488,11 @@ describe('Installations', () => { }; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => database.adapter.find('_Installation', {installationId: installId1}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {installationId: installId1}, {})) .then(results => { firstObject = results[0]; expect(results.length).toEqual(1); - return database.adapter.find('_Installation', {installationId: installId2}, installationSchema, {}); + return database.adapter.find('_Installation', installationSchema, {installationId: installId2}, {}); }).then(results => { expect(results.length).toEqual(1); secondObject = results[0]; @@ -503,7 +503,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', secondObject.objectId, input); }) - .then(() => database.adapter.find('_Installation', {objectId: firstObject.objectId}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {objectId: firstObject.objectId}, {})) .then(results => { // The first object should have been deleted expect(results.length).toEqual(0); @@ -530,11 +530,11 @@ describe('Installations', () => { }; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => database.adapter.find('_Installation', {installationId: installId1}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {installationId: installId1}, {})) .then((results) => { expect(results.length).toEqual(1); firstObject = results[0]; - return database.adapter.find('_Installation', {installationId: installId2}, installationSchema, {}); + return database.adapter.find('_Installation', installationSchema, {installationId: installId2}, {}); }) .then(results => { expect(results.length).toEqual(1); @@ -546,7 +546,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', secondObject.objectId, input); }) - .then(() => database.adapter.find('_Installation', {objectId: firstObject.objectId}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {objectId: firstObject.objectId}, {})) .then(results => { // The first object should have been deleted expect(results.length).toEqual(0); @@ -570,7 +570,7 @@ describe('Installations', () => { input.appIdentifier = 'bar'; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { // The first object should have been deleted during merge expect(results.length).toEqual(1); @@ -587,7 +587,7 @@ describe('Installations', () => { 'deviceType': 'ios' }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -596,7 +596,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); @@ -621,7 +621,7 @@ describe('Installations', () => { }; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => database.adapter.find('_Installation', { deviceToken: t }, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -631,7 +631,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); @@ -656,7 +656,7 @@ describe('Installations', () => { }; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => database.adapter.find('_Installation', { deviceToken: t }, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -670,7 +670,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); @@ -691,7 +691,7 @@ describe('Installations', () => { var installObj; var tokenObj; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); installObj = results[0]; @@ -701,7 +701,7 @@ describe('Installations', () => { }; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => database.adapter.find('_Installation', { deviceToken: t }, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {})) .then(results => { expect(results.length).toEqual(1); tokenObj = results[0]; @@ -712,7 +712,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', installObj.objectId, input); }) - .then(() => database.adapter.find('_Installation', { objectId: tokenObj.objectId }, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, { objectId: tokenObj.objectId }, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); @@ -731,7 +731,7 @@ describe('Installations', () => { var installObj; var tokenObj; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); installObj = results[0]; @@ -741,7 +741,7 @@ describe('Installations', () => { }; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => database.adapter.find('_Installation', { deviceToken: t }, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {})) .then(results => { expect(results.length).toEqual(1); tokenObj = results[0]; @@ -756,7 +756,7 @@ describe('Installations', () => { }; return rest.update(config, auth.nobody(config), '_Installation', installObj.objectId, input); }) - .then(() => database.adapter.find('_Installation', { objectId: tokenObj.objectId }, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, { objectId: tokenObj.objectId }, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); @@ -784,7 +784,7 @@ describe('Installations', () => { 'deviceType': 'ios' }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -794,7 +794,7 @@ describe('Installations', () => { }; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].deviceToken).toEqual(t); diff --git a/spec/RestCreate.spec.js b/spec/RestCreate.spec.js index c187ceeea2..55822cb406 100644 --- a/spec/RestCreate.spec.js +++ b/spec/RestCreate.spec.js @@ -13,7 +13,7 @@ let database = config.database; describe('rest create', () => { it('handles _id', done => { rest.create(config, auth.nobody(config), 'Foo', {}) - .then(() => database.adapter.find('Foo', {}, { fields: {} }, {})) + .then(() => database.adapter.find('Foo', { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; @@ -31,11 +31,11 @@ describe('rest create', () => { date: Parse._encode(now), }; rest.create(config, auth.nobody(config), 'MyClass', obj) - .then(() => database.adapter.find('MyClass', {}, { fields: { + .then(() => database.adapter.find('MyClass', { fields: { array: { type: 'Array' }, object: { type: 'Object' }, date: { type: 'Date' }, - } }, {})) + } }, {}, {})) .then(results => { expect(results.length).toEqual(1); var mob = results[0]; @@ -50,7 +50,7 @@ describe('rest create', () => { it('handles object and subdocument', done => { let obj = { subdoc: {foo: 'bar', wu: 'tan'} }; rest.create(config, auth.nobody(config), 'MyClass', obj) - .then(() => database.adapter.find('MyClass', {}, { fields: {} }, {})) + .then(() => database.adapter.find('MyClass', { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); let mob = results[0]; @@ -61,7 +61,7 @@ describe('rest create', () => { let obj = { 'subdoc.wu': 'clan' }; return rest.update(config, auth.nobody(config), 'MyClass', mob.objectId, obj) }) - .then(() => database.adapter.find('MyClass', {}, { fields: {} }, {})) + .then(() => database.adapter.find('MyClass', { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); let mob = results[0]; @@ -254,10 +254,10 @@ describe('rest create', () => { } }; rest.create(config, auth.nobody(config), 'APointerDarkly', obj) - .then(() => database.adapter.find('APointerDarkly', {}, { fields: { + .then(() => database.adapter.find('APointerDarkly', { fields: { foo: { type: 'String' }, aPointer: { type: 'Pointer', targetClass: 'JustThePointer' }, - }}, {})) + }}, {}, {})) .then(results => { expect(results.length).toEqual(1); let output = results[0]; diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js index 1fdfd95279..524451d996 100644 --- a/spec/ValidationAndPasswordsReset.spec.js +++ b/spec/ValidationAndPasswordsReset.spec.js @@ -561,7 +561,7 @@ describe("Password Reset", () => { Parse.User.logIn("zxcv", "hello").then(function(user){ let config = new Config('test'); - config.database.adapter.find('_User', { 'username': 'zxcv' }, { fields: {} }, { limit: 1 }) + config.database.adapter.find('_User', { fields: {} }, { 'username': 'zxcv' }, { limit: 1 }) .then(results => { // _perishable_token should be unset after reset password expect(results.length).toEqual(1); diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 9858dcb6ec..5cafac94e6 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -250,7 +250,7 @@ export class MongoStorageAdapter { } // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. - find(className, query, schema, { skip, limit, sort }) { + find(className, schema, query, { skip, limit, sort }) { let mongoWhere = transformWhere(className, query, schema); let mongoSort = _.mapKeys(sort, (value, fieldName) => transformKey(className, fieldName, schema)); return this._adaptiveCollection(className) diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index b43c34d3ad..bb8b54c291 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -449,14 +449,14 @@ function keysForQuery(query) { // Returns a promise for a list of related ids given an owning id. // className here is the owning className. DatabaseController.prototype.relatedIds = function(className, key, owningId) { - return this.adapter.find(joinTableName(className, key), { owningId }, relationSchema, {}) + return this.adapter.find(joinTableName(className, key), relationSchema, { owningId }, {}) .then(results => results.map(result => result.relatedId)); }; // Returns a promise for a list of owning ids given some related ids. // className here is the owning className. DatabaseController.prototype.owningIds = function(className, key, relatedIds) { - return this.adapter.find(joinTableName(className, key), { relatedId: { '$in': relatedIds } }, relationSchema, {}) + return this.adapter.find(joinTableName(className, key), relationSchema, { relatedId: { '$in': relatedIds } }, {}) .then(results => results.map(result => result.owningId)); }; @@ -686,7 +686,7 @@ DatabaseController.prototype.find = function(className, query, { if (count) { return this.adapter.count(className, query, schema); } else { - return this.adapter.find(className, query, schema, { skip, limit, sort }) + return this.adapter.find(className, schema, query, { skip, limit, sort }) .then(objects => objects.map(object => { object = untransformObjectACL(object); return filterSensitiveData(isMaster, aclGroup, className, object) From 1a551d0542e7808a8b194cf2c7c1263c3f18f1b7 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 18:32:50 -0700 Subject: [PATCH 25/44] finishsh touching up argument order --- spec/ParseAPI.spec.js | 2 +- spec/Uniqueness.spec.js | 10 +++++----- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 4 ++-- src/Controllers/DatabaseController.js | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 40c99c1f85..b28b0047e1 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -213,7 +213,7 @@ describe('miscellaneous', function() { it('ensure that if you try to sign up a user with a unique username and email, but duplicates in some other field that has a uniqueness constraint, you get a regular duplicate value error', done => { let config = new Config('test'); - config.database.adapter.ensureUniqueness('_User', ['randomField'], requiredUserFields) + config.database.adapter.ensureUniqueness('_User', requiredUserFields, ['randomField']) .then(() => { let user = new Parse.User(); user.setPassword('asdf'); diff --git a/spec/Uniqueness.spec.js b/spec/Uniqueness.spec.js index fe7e1b88b5..129ec07599 100644 --- a/spec/Uniqueness.spec.js +++ b/spec/Uniqueness.spec.js @@ -11,7 +11,7 @@ describe('Uniqueness', function() { obj.save().then(() => { expect(obj.id).not.toBeUndefined(); let config = new Config('test'); - return config.database.adapter.ensureUniqueness('UniqueField', ['unique'], { fields: { unique: { __type: 'String' } } }) + return config.database.adapter.ensureUniqueness('UniqueField', { fields: { unique: { __type: 'String' } } }, ['unique']) }) .then(() => { let obj = new Parse.Object('UniqueField'); @@ -32,10 +32,10 @@ describe('Uniqueness', function() { .then(() => obj.save({ ptr: obj })) .then(() => { let config = new Config('test'); - return config.database.adapter.ensureUniqueness('UniquePointer', ['ptr'], { fields: { + return config.database.adapter.ensureUniqueness('UniquePointer', { fields: { string: { __type: 'String' }, ptr: { __type: 'Pointer', targetClass: 'UniquePointer' } - } }); + } }, ['ptr']); }) .then(() => { let newObj = new Parse.Object('UniquePointer') @@ -60,7 +60,7 @@ describe('Uniqueness', function() { Parse.Object.saveAll([o1, o2]) .then(() => { let config = new Config('test'); - return config.database.adapter.ensureUniqueness('UniqueFail', ['key'], { fields: { key: { __type: 'String' } } }); + return config.database.adapter.ensureUniqueness('UniqueFail', { fields: { key: { __type: 'String' } } }, ['key']); }) .catch(error => { expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); @@ -70,7 +70,7 @@ describe('Uniqueness', function() { it('can do compound uniqueness', done => { let config = new Config('test'); - config.database.adapter.ensureUniqueness('CompoundUnique', ['k1', 'k2'], { fields: { k1: { __type: 'String' }, k2: { __type: 'String' } } }) + config.database.adapter.ensureUniqueness('CompoundUnique', { fields: { k1: { __type: 'String' }, k2: { __type: 'String' } } }, ['k1', 'k2']) .then(() => { let o1 = new Parse.Object('CompoundUnique'); o1.set('k1', 'v1'); diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 5cafac94e6..55710aaa1f 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -263,7 +263,7 @@ export class MongoStorageAdapter { // As such, we shouldn't expose this function to users of parse until we have an out-of-band // Way of determining if a field is nullable. Undefined doesn't count against uniqueness, // which is why we use sparse indexes. - ensureUniqueness(className, fieldNames, schema) { + ensureUniqueness(className, schema, fieldNames) { let indexCreationRequest = {}; let mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema)); mongoFieldNames.forEach(fieldName => { @@ -286,7 +286,7 @@ export class MongoStorageAdapter { } // Executs a count. - count(className, query, schema) { + count(className, schema, query) { return this._adaptiveCollection(className) .then(collection => collection.count(transformWhere(className, query, schema))); } diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index bb8b54c291..8918860731 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -684,7 +684,7 @@ DatabaseController.prototype.find = function(className, query, { } validateQuery(query); if (count) { - return this.adapter.count(className, query, schema); + return this.adapter.count(className, schema, query); } else { return this.adapter.find(className, schema, query, { skip, limit, sort }) .then(objects => objects.map(object => { From 71944b9382f374c9e9c2de0609e2d4dd14f476f9 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 2 Jun 2016 23:02:42 -0700 Subject: [PATCH 26/44] Accept a database adapter as a parameter --- spec/ParseAPI.spec.js | 2 +- spec/ParseHooks.spec.js | 1 + spec/ParseInstallation.spec.js | 6 +++--- src/Controllers/DatabaseController.js | 6 +----- src/ParseServer.js | 1 + 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index b28b0047e1..8eabf8886a 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -383,7 +383,7 @@ describe('miscellaneous', function() { return obj.save(); }).then(() => { let config = new Config(appId); - return config.database.adapter.find('TestObject', {}, { fields: {} }, {}); + return config.database.adapter.find('TestObject', { fields: {} }, {}, {}); }).then((results) => { expect(results.length).toEqual(1); expect(results[0]['foo']).toEqual('bar'); diff --git a/spec/ParseHooks.spec.js b/spec/ParseHooks.spec.js index 1a1349dae2..8049722423 100644 --- a/spec/ParseHooks.spec.js +++ b/spec/ParseHooks.spec.js @@ -10,6 +10,7 @@ Parse.Hooks = require("../src/cloud-code/Parse.Hooks"); var port = 12345; var hookServerURL = "http://localhost:"+port; +let AppCache = require('../src/cache').AppCache; var app = express(); app.use(bodyParser.json({ 'type': '*/*' })) diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index 7663e9a48f..3d1e45d67b 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -24,7 +24,7 @@ describe('Installations', () => { 'deviceType': device }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => config.database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; @@ -42,7 +42,7 @@ describe('Installations', () => { 'deviceType': device }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => config.database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; @@ -60,7 +60,7 @@ describe('Installations', () => { 'deviceType': device }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => config.database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); var obj = results[0]; diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 8918860731..a0b3e75212 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -84,13 +84,9 @@ function DatabaseController(adapter, { skipValidation } = {}) { } DatabaseController.prototype.WithoutValidation = function() { - return new DatabaseController(this.adapter, {collectionPrefix: this.collectionPrefix, skipValidation: true}); + return new DatabaseController(this.adapter, { skipValidation: true }); } -DatabaseController.prototype.schemaCollection = function() { - return this.adapter.schemaCollection(); -}; - DatabaseController.prototype.collectionExists = function(className) { return this.adapter.classExists(className); }; diff --git a/src/ParseServer.js b/src/ParseServer.js index b823b00615..918a8cb3ed 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -101,6 +101,7 @@ class ParseServer { logsFolder, databaseURI, databaseOptions, + databaseAdapter, cloud, collectionPrefix = '', clientKey, From a3a9678bb69a56153b7474bd2bfc01763b9213f8 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Sat, 4 Jun 2016 00:01:28 -0700 Subject: [PATCH 27/44] First passing test with postgres! --- package.json | 1 + spec/ParseAPI.spec.js | 7 +- spec/helper.js | 19 +- .../Postgres/PostgresStorageAdapter.js | 270 ++++++++++++++++++ src/Controllers/SchemaController.js | 2 +- src/Routers/SchemasRouter.js | 2 +- 6 files changed, 292 insertions(+), 9 deletions(-) create mode 100644 src/Adapters/Storage/Postgres/PostgresStorageAdapter.js diff --git a/package.json b/package.json index 8fb67faf8b..44130aa77e 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "parse-server-push-adapter": "^1.0.0", "parse-server-s3-adapter": "^1.0.1", "parse-server-simple-mailgun-adapter": "^1.0.0", + "pg-promise": "^4.4.0", "redis": "^2.5.0-1", "request": "^2.65.0", "request-promise": "^3.0.0", diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 8eabf8886a..a41ad5d5d5 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -13,14 +13,17 @@ var TestUtils = require('../src/index').TestUtils; const requiredUserFields = { fields: Object.assign({}, defaultColumns._Default, defaultColumns._User) }; describe('miscellaneous', function() { - it('create a GameScore object', function(done) { + fit('create a GameScore object', function(done) { var obj = new Parse.Object('GameScore'); obj.set('score', 1337); obj.save().then(function(obj) { expect(typeof obj.id).toBe('string'); expect(typeof obj.createdAt.toGMTString()).toBe('string'); done(); - }, function(err) { console.log(err); }); + }, error => { + fail(JSON.stringify(error)); + done(); + }); }); it('get a TestObject', function(done) { diff --git a/spec/helper.js b/spec/helper.js index 273d0d4935..417223b3d5 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -11,23 +11,32 @@ var ParseServer = require('../src/index').ParseServer; var path = require('path'); var TestUtils = require('../src/index').TestUtils; var MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter'); -const GridStoreAdapter = require('../src/Adapters/Files/GridStoreAdapter').GridStoreAdapter; -var port = 8378; +const GridStoreAdapter = require('../src/Adapters/Files/GridStoreAdapter').GridStoreAdapter; +const PostgresStorageAdapter = require('../src/Adapters/Storage/Postgres/PostgresStorageAdapter'); -let mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; -let mongoAdapter = new MongoStorageAdapter({ +var mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; +var mongoAdapter = new MongoStorageAdapter({ uri: mongoURI, collectionPrefix: 'test_', }) +var postgresURI = 'postgres://localhost:5432/drewgross'; +var postgresAdapter = new PostgresStorageAdapter({ + uri: postgresURI, + collectionPrefix: 'test_', +}); + +var port = 8378; + let gridStoreAdapter = new GridStoreAdapter(mongoURI); // Default server configuration for tests. var defaultConfiguration = { - databaseAdapter: mongoAdapter, filesAdapter: gridStoreAdapter, serverURL: 'http://localhost:' + port + '/1', + databaseAdapter: mongoAdapter, + databaseAdapter: postgresAdapter, appId: 'test', javascriptKey: 'test', dotNetKey: 'windows', diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js new file mode 100644 index 0000000000..b4034609c5 --- /dev/null +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -0,0 +1,270 @@ +const pgp = require('pg-promise')(); + +const PostgresRelationDoesNotExistError = '42P01'; +const PostgresDuplicateRelationError = '42P07'; + + +export class PostgresStorageAdapter { + // Private + _collectionPrefix: string; + _client; + + constructor({ + uri, + collectionPrefix = '', + }) { + this._collectionPrefix = collectionPrefix; + this._client = pgp(uri); + } + + _ensureSchemaCollectionExists() { + return this._client.query('CREATE TABLE "_SCHEMA" ( "className" varChar(120), "schema" jsonb, "isParseClass" bool, PRIMARY KEY ("className") )') + .catch(error => { + if (error.code === PostgresDuplicateRelationError) { + // Table already exists, must have been created by a different request. Ignore error. + return; + } else { + throw error; + } + }); + }; + + classExists(name) { + console.log('classExists(name) {') + return this.connect().then(() => { + return this.database.listCollections({ name: this._collectionPrefix + name }).toArray(); + }).then(collections => { + return collections.length > 0; + }); + } + + setClassLevelPermissions(className, CLPs) { + console.log('setClassLevelPermissions(className, CLPs) {') + return this._schemaCollection() + .then(schemaCollection => schemaCollection.updateSchema(className, { + $set: { _metadata: { class_permissions: CLPs } } + })); + } + + createClass(className, schema) { + return this._client.query('CREATE TABLE "GameScore" ()', {}) + .then(() => this._client.query('INSERT INTO "_SCHEMA" VALUES ($, $, true)', { className, schema })) + } + + addFieldIfNotExists(className, fieldName, type) { + // TODO: Doing this in a transaction is probably a good idea. + return this._client.query('ALTER TABLE "GameScore" ADD COLUMN "score" double precision', { className, fieldName }) + .catch(error => { + if (error.code === PostgresRelationDoesNotExistError) { + return this.createClass(className, { fields: { [fieldName]: type } }) + } else { + throw error; + } + }) + .then(() => this._client.query('SELECT "schema" FROM "_SCHEMA"', { className })) + .then(result => { + if (fieldName in result[0].schema) { + throw "Attempted to add a field that already exists"; + } else { + result[0].schema.fields[fieldName] = type; + return this._client.query( + 'UPDATE "_SCHEMA" SET "schema"=$ WHERE "className"=$', + { schema: result[0].schema, className } + ); + } + }) + } + + // Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.) + // and resolves with false if it wasn't (eg. a join table). Rejects if deletion was impossible. + deleteClass(className) { + console.log('deleteClass(className) {') + return this._adaptiveCollection(className) + .then(collection => collection.drop()) + .catch(error => { + // 'ns not found' means collection was already gone. Ignore deletion attempt. + if (error.message == 'ns not found') { + return; + } + throw error; + }) + // We've dropped the collection, now remove the _SCHEMA document + .then(() => this._schemaCollection()) + .then(schemaCollection => schemaCollection.findAndDeleteSchema(className)) + } + + // Delete all data known to this adatper. Used for testing. + deleteAllClasses() { + return this._client.query('SELECT "className" FROM "_SCHEMA"') + .then(results => { + const classes = ['_SCHEMA', ...results.map(result => result.className)]; + return Promise.all(classes.map(className => this._client.query('DROP TABLE $', { className }))); + }, error => { + if (error.code === PostgresRelationDoesNotExistError) { + // No _SCHEMA collection. Don't delete anything. + return; + } else { + throw error; + } + }) + } + + // Remove the column and all the data. For Relations, the _Join collection is handled + // specially, this function does not delete _Join columns. It should, however, indicate + // that the relation fields does not exist anymore. In mongo, this means removing it from + // the _SCHEMA collection. There should be no actual data in the collection under the same name + // as the relation column, so it's fine to attempt to delete it. If the fields listed to be + // deleted do not exist, this function should return successfully anyways. Checking for + // attempts to delete non-existent fields is the responsibility of Parse Server. + + // Pointer field names are passed for legacy reasons: the original mongo + // format stored pointer field names differently in the database, and therefore + // needed to know the type of the field before it could delete it. Future database + // adatpers should ignore the pointerFieldNames argument. All the field names are in + // fieldNames, they show up additionally in the pointerFieldNames database for use + // by the mongo adapter, which deals with the legacy mongo format. + + // This function is not obligated to delete fields atomically. It is given the field + // names in a list so that databases that are capable of deleting fields atomically + // may do so. + + // Returns a Promise. + deleteFields(className, schema, fieldNames) { + console.log('deleteFields(className, schema, fieldNames) {') + const mongoFormatNames = fieldNames.map(fieldName => { + if (schema.fields[fieldName].type === 'Pointer') { + return `_p_${fieldName}` + } else { + return fieldName; + } + }); + const collectionUpdate = { '$unset' : {} }; + mongoFormatNames.forEach(name => { + collectionUpdate['$unset'][name] = null; + }); + + const schemaUpdate = { '$unset' : {} }; + fieldNames.forEach(name => { + schemaUpdate['$unset'][name] = null; + }); + + return this._adaptiveCollection(className) + .then(collection => collection.updateMany({}, collectionUpdate)) + .then(() => this._schemaCollection()) + .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate)); + } + + // Return a promise for all schemas known to this adapter, in Parse format. In case the + // schemas cannot be retrieved, returns a promise that rejects. Requirements for the + // rejection reason are TBD. + getAllClasses() { + return this._ensureSchemaCollectionExists() + .then(() => this._client.query('SELECT * FROM "_SCHEMA"')) + .then(results => results.map(result => ({ className: result.className, ...result.schema }))) + } + + // Return a promise for the schema with the given name, in Parse format. If + // this adapter doesn't know about the schema, return a promise that rejects with + // undefined as the reason. + getClass(className) { + return this._client.query('SELECT * FROM "_SCHEMA" WHERE "className"=$', { className }) + .then(result => { + if (result.length === 1) { + return result; + } else { + throw undefined; + } + }); + } + + // TODO: remove the mongo format dependency + createObject(className, schema, object) { + return this._client.query('INSERT INTO "GameScore" (score) VALUES ($)', { score: object.score }) + .then(() => ({ ops: [object] })); + } + + // Remove all objects that match the given Parse Query. + // If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined. + // If there is some other error, reject with INTERNAL_SERVER_ERROR. + deleteObjectsByQuery(className, schema, query) { + console.log('deleteObjectsByQuery(className, schema, query) {') + return this._adaptiveCollection(className) + .then(collection => { + let mongoWhere = transformWhere(className, query, schema); + return collection.deleteMany(mongoWhere) + }) + .then(({ result }) => { + if (result.n === 0) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); + } + return Promise.resolve(); + }, error => { + throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error'); + }); + } + + // Apply the update to all objects that match the given Parse Query. + updateObjectsByQuery(className, schema, query, update) { + console.log('updateObjectsByQuery(className, schema, query, update) {') + const mongoUpdate = transformUpdate(className, update, schema); + const mongoWhere = transformWhere(className, query, schema); + return this._adaptiveCollection(className) + .then(collection => collection.updateMany(mongoWhere, mongoUpdate)); + } + + // Hopefully we can get rid of this in favor of updateObjectsByQuery. + findOneAndUpdate(className, schema, query, update) { + console.log('findOneAndUpdate(className, schema, query, update) {') + const mongoUpdate = transformUpdate(className, update, schema); + const mongoWhere = transformWhere(className, query, schema); + return this._adaptiveCollection(className) + .then(collection => collection.findOneAndUpdate(mongoWhere, mongoUpdate)); + } + + // Hopefully we can get rid of this. It's only used for config and hooks. + upsertOneObject(className, schema, query, update) { + console.log('upsertOneObject(className, schema, query, update) {') + const mongoUpdate = transformUpdate(className, update, schema); + const mongoWhere = transformWhere(className, query, schema); + return this._adaptiveCollection(className) + .then(collection => collection.upsertOne(mongoWhere, mongoUpdate)); + } + + // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. + find(className, schema, query, { skip, limit, sort }) { + return this._client.query("SELECT * FROM $", { className }) + } + + // Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't + // currently know which fields are nullable and which aren't, we ignore that criteria. + // As such, we shouldn't expose this function to users of parse until we have an out-of-band + // Way of determining if a field is nullable. Undefined doesn't count against uniqueness, + // which is why we use sparse indexes. + ensureUniqueness(className, schema, fieldNames) { + console.log('ensureUniqueness(className, schema, fieldNames) {') + let indexCreationRequest = {}; + let mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema)); + mongoFieldNames.forEach(fieldName => { + indexCreationRequest[fieldName] = 1; + }); + return this._adaptiveCollection(className) + .then(collection => collection._ensureSparseUniqueIndexInBackground(indexCreationRequest)) + .catch(error => { + if (error.code === 11000) { + throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Tried to ensure field uniqueness for a class that already has duplicates.'); + } else { + throw error; + } + }); + } + + // Executs a count. + count(className, schema, query) { + console.log('count(className, schema, query) {') + return this._adaptiveCollection(className) + .then(collection => collection.count(transformWhere(className, query, schema))); + } +} + +export default PostgresStorageAdapter; +module.exports = PostgresStorageAdapter; // Required for tests diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index 649dc66a46..562e3c7649 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -381,7 +381,7 @@ class SchemaController { 'schema is frozen, cannot add: ' + className); } // We don't have this class. Update the schema - return this.addClassIfNotExists(className, []).then(() => { + return this.addClassIfNotExists(className, {}).then(() => { // The schema update succeeded. Reload the schema return this.reloadData(); }, () => { diff --git a/src/Routers/SchemasRouter.js b/src/Routers/SchemasRouter.js index 35b5d92dda..4024d0a2db 100644 --- a/src/Routers/SchemasRouter.js +++ b/src/Routers/SchemasRouter.js @@ -47,7 +47,7 @@ function createSchema(req) { } return req.config.database.loadSchema() - .then(schema => schema.addClassIfNotExists(className, req.body.fields, req.body.classLevelPermissions)) + .then(schema => schema.addClassIfNotExists(className, req.body.fields, req.body.classLevelPermissions)) .then(schema => ({ response: schema })); } From 523dd922f7e8a0b529b04a930847b65f283d8248 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Mon, 6 Jun 2016 09:39:38 -0700 Subject: [PATCH 28/44] Actually use the provided className --- src/Adapters/Storage/Postgres/PostgresStorageAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index b4034609c5..b5a91528d0 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -47,8 +47,8 @@ export class PostgresStorageAdapter { } createClass(className, schema) { - return this._client.query('CREATE TABLE "GameScore" ()', {}) - .then(() => this._client.query('INSERT INTO "_SCHEMA" VALUES ($, $, true)', { className, schema })) + return this._client.query('CREATE TABLE $ ()', { className }) + .then(() => this._client.query('INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($, $, true)', { className, schema })) } addFieldIfNotExists(className, fieldName, type) { From 2794aea6e993f773f0c624485cbeb0641c0cd457 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 16:30:26 -0700 Subject: [PATCH 29/44] Fix tests --- spec/ParseAPI.spec.js | 92 ++++++++---------------- spec/ParseHooks.spec.js | 1 - spec/ValidationAndPasswordsReset.spec.js | 4 ++ spec/helper.js | 2 +- src/ParseServer.js | 5 +- 5 files changed, 37 insertions(+), 67 deletions(-) diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index a41ad5d5d5..55c7ec5974 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -13,7 +13,7 @@ var TestUtils = require('../src/index').TestUtils; const requiredUserFields = { fields: Object.assign({}, defaultColumns._Default, defaultColumns._User) }; describe('miscellaneous', function() { - fit('create a GameScore object', function(done) { + it('create a GameScore object', function(done) { var obj = new Parse.Object('GameScore'); obj.set('score', 1337); obj.save().then(function(obj) { @@ -128,89 +128,57 @@ describe('miscellaneous', function() { }); it('ensure that if people already have duplicate users, they can still sign up new users', done => { + let config = new Config('test'); // Remove existing data to clear out unique index TestUtils.destroyAllDataPermanently() - let config = new Config('test'); .then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'x', username: 'u' })) .then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'y', username: 'u' })) - .then(() => { + // Create a new server to try to recreate the unique indexes + .then(reconfigureServer) + .catch(() => { let user = new Parse.User(); user.setPassword('asdf'); user.setUsername('zxcv'); - return user.signUp(); + // Sign up with new email still works + return user.signUp().catch(fail); }) .then(() => { - let adapter = new MongoStorageAdapter({ - uri: 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase', - collectionPrefix: 'test_', - }); - adapter.createObject('_User', { objectId: 'x', username: 'u' }, requiredUserFields) - .then(() => adapter.createObject('_User', { objectId: 'y', username: 'u' }, requiredUserFields)) - .then(() => { - let user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - return user.signUp(); - }) - .then(() => { - let user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('u'); - user.signUp() - .catch(error => { - expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN); - done(); - }); - }) - .catch(error => { - fail(JSON.stringify(error)); - done(); - }); - }, () => { - fail('destroyAllDataPermanently failed') + let user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('u'); + // sign up with duplicate username doens't + return user.signUp() + }) + .catch(error => { + expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN); done(); - }); + }) }); it('ensure that if people already have duplicate emails, they can still sign up new users', done => { - // Wipe out existing database with unique index so we can create a duplicate user + let config = new Config('test'); + // Remove existing data to clear out unique index TestUtils.destroyAllDataPermanently() - .then(TestUtils.destroyAllDataPermanently) .then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'x', email: 'a@b.c' })) .then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'y', email: 'a@b.c' })) - .then(() => { + .then(reconfigureServer) + .catch(() => { let user = new Parse.User(); user.setPassword('asdf'); user.setUsername('qqq'); user.setEmail('unique@unique.unique'); - return user.signUp(); + return user.signUp().catch(fail); }) .then(() => { - let config = new Config('test'); - config.database.adapter.createObject('_User', { objectId: 'x', email: 'a@b.c' }, requiredUserFields) - .then(() => config.database.adapter.createObject('_User', { objectId: 'y', email: 'a@b.c' }, requiredUserFields)) - .then(() => { - let user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('qqq'); - user.setEmail('unique@unique.unique'); - return user.signUp(); - }) - .then(() => { - let user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('www'); - user.setEmail('a@b.c'); - user.signUp() - .catch(error => { - expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); - done(); - }); - }) - .catch(error => { - fail(JSON.stringify(error)); - done(); - }); + let user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('www'); + user.setEmail('a@b.c'); + return user.signUp() + }) + .catch(error => { + expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); + done(); }); }); diff --git a/spec/ParseHooks.spec.js b/spec/ParseHooks.spec.js index 8049722423..75b5857ea7 100644 --- a/spec/ParseHooks.spec.js +++ b/spec/ParseHooks.spec.js @@ -15,7 +15,6 @@ let AppCache = require('../src/cache').AppCache; var app = express(); app.use(bodyParser.json({ 'type': '*/*' })) app.listen(12345); -let AppCache = require('../src/cache').AppCache; describe('Hooks', () => { diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js index 524451d996..f529dcd0a2 100644 --- a/spec/ValidationAndPasswordsReset.spec.js +++ b/spec/ValidationAndPasswordsReset.spec.js @@ -265,6 +265,10 @@ describe("Email Verification", () => { expect(error.message).toEqual('An appName, publicServerURL, and emailAdapter are required for password reset functionality.') done(); }); + }) + .catch(error => { + fail(JSON.stringify(error)); + done(); }); }); diff --git a/spec/helper.js b/spec/helper.js index 417223b3d5..f7c071ece1 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -35,8 +35,8 @@ let gridStoreAdapter = new GridStoreAdapter(mongoURI); var defaultConfiguration = { filesAdapter: gridStoreAdapter, serverURL: 'http://localhost:' + port + '/1', - databaseAdapter: mongoAdapter, databaseAdapter: postgresAdapter, + databaseAdapter: mongoAdapter, appId: 'test', javascriptKey: 'test', dotNetKey: 'windows', diff --git a/src/ParseServer.js b/src/ParseServer.js index 918a8cb3ed..7c6ae6c9f1 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -95,7 +95,6 @@ class ParseServer { masterKey = requiredParameter('You must provide a masterKey!'), appName, filesAdapter, - databaseAdapter, push, loggerAdapter, logsFolder, @@ -194,13 +193,13 @@ class ParseServer { const databaseController = new DatabaseController(databaseAdapter); const hooksController = new HooksController(appId, databaseController, webhookKey); - let usernameUniqueness = databaseController.adapter.ensureUniqueness('_User', ['username'], requiredUserFields) + let usernameUniqueness = databaseController.adapter.ensureUniqueness('_User', requiredUserFields, ['username']) .catch(error => { logger.warn('Unable to ensure uniqueness for usernames: ', error); return Promise.reject(); }); - let emailUniqueness = databaseController.adapter.ensureUniqueness('_User', ['email'], requiredUserFields) + let emailUniqueness = databaseController.adapter.ensureUniqueness('_User', requiredUserFields, ['email']) .catch(error => { logger.warn('Unabled to ensure uniqueness for user email addresses: ', error); return Promise.reject(); From 1adc5d501ab36d37323a7ae841244f83357a57ee Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 17:07:15 -0700 Subject: [PATCH 30/44] Setup travis --- .travis.yml | 8 ++++++ spec/ParseAPI.spec.js | 2 +- spec/helper.js | 27 ++++++++++--------- .../Postgres/PostgresStorageAdapter.js | 1 + 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2b5c1e1ef2..1ac6fb823e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,15 @@ language: node_js node_js: - '4.3' - '6.1' +services: +- postgresql +addons: +- postgresql: '9.5' +before_script: +- psql -c 'create database parse_server_postgres_adapter_test_database;' -U postgres env: + - PARSE_SERVER_TEST_DB=postgres + - PARSE_SERVER_TEST_DB=mongodb global: - COVERAGE_OPTION='./node_modules/babel-istanbul/lib/cli.js cover -x **/spec/**' matrix: diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 55c7ec5974..b38f35c461 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -13,7 +13,7 @@ var TestUtils = require('../src/index').TestUtils; const requiredUserFields = { fields: Object.assign({}, defaultColumns._Default, defaultColumns._User) }; describe('miscellaneous', function() { - it('create a GameScore object', function(done) { + fit('create a GameScore object', function(done) { var obj = new Parse.Object('GameScore'); obj.set('score', 1337); obj.save().then(function(obj) { diff --git a/spec/helper.js b/spec/helper.js index f7c071ece1..468546378b 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -15,17 +15,21 @@ var MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAda const GridStoreAdapter = require('../src/Adapters/Files/GridStoreAdapter').GridStoreAdapter; const PostgresStorageAdapter = require('../src/Adapters/Storage/Postgres/PostgresStorageAdapter'); -var mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; -var mongoAdapter = new MongoStorageAdapter({ - uri: mongoURI, - collectionPrefix: 'test_', -}) +const mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; +let databaseAdapter; +if (process.env.PARSE_SERVER_TEST_DB === 'postgres') { + var postgresURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; + databaseAdapter = new PostgresStorageAdapter({ + uri: postgresURI, + collectionPrefix: 'test_', + }); +} else { + databaseAdapter = new MongoStorageAdapter({ + uri: mongoURI, + collectionPrefix: 'test_', + }) +} -var postgresURI = 'postgres://localhost:5432/drewgross'; -var postgresAdapter = new PostgresStorageAdapter({ - uri: postgresURI, - collectionPrefix: 'test_', -}); var port = 8378; @@ -35,8 +39,7 @@ let gridStoreAdapter = new GridStoreAdapter(mongoURI); var defaultConfiguration = { filesAdapter: gridStoreAdapter, serverURL: 'http://localhost:' + port + '/1', - databaseAdapter: postgresAdapter, - databaseAdapter: mongoAdapter, + databaseAdapter, appId: 'test', javascriptKey: 'test', dotNetKey: 'windows', diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index b5a91528d0..deaec85f7b 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -242,6 +242,7 @@ export class PostgresStorageAdapter { // which is why we use sparse indexes. ensureUniqueness(className, schema, fieldNames) { console.log('ensureUniqueness(className, schema, fieldNames) {') + return Promise.resolve(); let indexCreationRequest = {}; let mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema)); mongoFieldNames.forEach(fieldName => { From 57b2b423403984e3a5954730ae4680411871e3cd Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 17:34:10 -0700 Subject: [PATCH 31/44] fix travis maybe --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ac6fb823e..e2dedbc7bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,15 +5,15 @@ node_js: services: - postgresql addons: -- postgresql: '9.5' + postgresql: '9.5' before_script: - psql -c 'create database parse_server_postgres_adapter_test_database;' -U postgres env: - - PARSE_SERVER_TEST_DB=postgres - - PARSE_SERVER_TEST_DB=mongodb global: - COVERAGE_OPTION='./node_modules/babel-istanbul/lib/cli.js cover -x **/spec/**' matrix: + - PARSE_SERVER_TEST_DB=postgres + - PARSE_SERVER_TEST_DB=mongodb - MONGODB_VERSION=2.6.11 - MONGODB_VERSION=3.0.8 - MONGODB_VERSION=3.2.6 From d7816878dd3b10f4448de31b5c6a5c3cdf64da42 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 17:45:40 -0700 Subject: [PATCH 32/44] try removing db user --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e2dedbc7bd..10d81c490b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ services: addons: postgresql: '9.5' before_script: -- psql -c 'create database parse_server_postgres_adapter_test_database;' -U postgres +- psql -c 'create database parse_server_postgres_adapter_test_database;' env: global: - COVERAGE_OPTION='./node_modules/babel-istanbul/lib/cli.js cover -x **/spec/**' From 054f5be70bce6f86a0229c6ea27f40e8747a065a Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 18:11:29 -0700 Subject: [PATCH 33/44] indentation? --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 10d81c490b..d3dee948e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ node_js: - '4.3' - '6.1' services: -- postgresql + - postgresql addons: postgresql: '9.5' before_script: From b78de364db318874a4a587a6e0874aefc4df8d5b Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 18:16:20 -0700 Subject: [PATCH 34/44] remove postgres version setting --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d3dee948e4..90438b27b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,8 @@ node_js: - '6.1' services: - postgresql -addons: - postgresql: '9.5' before_script: -- psql -c 'create database parse_server_postgres_adapter_test_database;' + - psql -c 'create database parse_server_postgres_adapter_test_database;' env: global: - COVERAGE_OPTION='./node_modules/babel-istanbul/lib/cli.js cover -x **/spec/**' From bd5bf2192f1886fc9663fcdbcc33a797b6543df6 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 22:43:03 -0700 Subject: [PATCH 35/44] sudo maybe? --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 90438b27b2..ca53b717fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: node_js node_js: - '4.3' - '6.1' +sudo: required services: - postgresql before_script: From 88945a4b8532cfa82db4d8a22655250109a25fa2 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 22:54:38 -0700 Subject: [PATCH 36/44] use postgres username --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ca53b717fe..8f8e396561 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,10 @@ language: node_js node_js: - '4.3' - '6.1' -sudo: required services: - postgresql before_script: - - psql -c 'create database parse_server_postgres_adapter_test_database;' + - psql -c 'create database parse_server_postgres_adapter_test_database;' -U postgres env: global: - COVERAGE_OPTION='./node_modules/babel-istanbul/lib/cli.js cover -x **/spec/**' From f2b1642b54d60d3509f051808e53de531fe6457f Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 23:00:00 -0700 Subject: [PATCH 37/44] fix check for _PushStatus --- spec/helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/helper.js b/spec/helper.js index 468546378b..472eea983e 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -143,7 +143,7 @@ beforeEach(done => { afterEach(function(done) { Parse.Cloud._removeAllHooks(); - mongoAdapter.getAllClasses() + databaseAdapter.getAllClasses() .then(allSchemas => { allSchemas.forEach((schema) => { var className = schema.className; From ecb5ac7bcfe67a54179a038147c8f22851f3eff6 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 23:12:40 -0700 Subject: [PATCH 38/44] excludes --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8f8e396561..b0bd66615a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,14 @@ env: - MONGODB_VERSION=2.6.11 - MONGODB_VERSION=3.0.8 - MONGODB_VERSION=3.2.6 +matrix: + exclude: + - PARSE_SERVER_TEST_DB=postgres + MONGODB_VERSION=2.6.11 + - PARSE_SERVER_TEST_DB=postgres + MONGODB_VERSION=3.0.8 + - PARSE_SERVER_TEST_DB=postgres + MONGODB_VERSION=3.2.6 branches: only: - master From 8df39896e882d78bd8996f41accb4ce66a4b41fc Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 23:15:18 -0700 Subject: [PATCH 39/44] remove db=mongo --- .travis.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index b0bd66615a..ec203ce65e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,18 +11,9 @@ env: - COVERAGE_OPTION='./node_modules/babel-istanbul/lib/cli.js cover -x **/spec/**' matrix: - PARSE_SERVER_TEST_DB=postgres - - PARSE_SERVER_TEST_DB=mongodb - MONGODB_VERSION=2.6.11 - MONGODB_VERSION=3.0.8 - MONGODB_VERSION=3.2.6 -matrix: - exclude: - - PARSE_SERVER_TEST_DB=postgres - MONGODB_VERSION=2.6.11 - - PARSE_SERVER_TEST_DB=postgres - MONGODB_VERSION=3.0.8 - - PARSE_SERVER_TEST_DB=postgres - MONGODB_VERSION=3.2.6 branches: only: - master From 41f7352df98cb765434725543c59868cf3c5bcab Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 23:33:10 -0700 Subject: [PATCH 40/44] allow postgres to fail --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index ec203ce65e..f34a35102d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,10 @@ env: - MONGODB_VERSION=2.6.11 - MONGODB_VERSION=3.0.8 - MONGODB_VERSION=3.2.6 +matrix: + fast_finish: true, + allow_failures: + - PARSE_SERVER_TEST_DB=postgres branches: only: - master From 96c09e67b299c927f6eb40458cc4e2e11863f66a Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 8 Jun 2016 23:44:14 -0700 Subject: [PATCH 41/44] Fix allow failure --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f34a35102d..1dba4b8c2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ env: matrix: fast_finish: true, allow_failures: - - PARSE_SERVER_TEST_DB=postgres + - env: PARSE_SERVER_TEST_DB=postgres branches: only: - master From 19b37208bbfe90c2dfeff900c8cf8cccdf9abb3d Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Thu, 9 Jun 2016 00:04:45 -0700 Subject: [PATCH 42/44] postgres 9.4 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1dba4b8c2d..bdf037e583 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ node_js: - '6.1' services: - postgresql +addons: + postgresql: '9.4' before_script: - psql -c 'create database parse_server_postgres_adapter_test_database;' -U postgres env: From 2e9127bbf6fe8841012855042c5c98bd9d7b6bf2 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Sat, 11 Jun 2016 11:28:00 -0700 Subject: [PATCH 43/44] Remove mongo implementations and fix test --- spec/ParseAPI.spec.js | 7 +- .../Postgres/PostgresStorageAdapter.js | 104 ++---------------- 2 files changed, 12 insertions(+), 99 deletions(-) diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index b38f35c461..63f5808574 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -13,7 +13,7 @@ var TestUtils = require('../src/index').TestUtils; const requiredUserFields = { fields: Object.assign({}, defaultColumns._Default, defaultColumns._User) }; describe('miscellaneous', function() { - fit('create a GameScore object', function(done) { + it('create a GameScore object', function(done) { var obj = new Parse.Object('GameScore'); obj.set('score', 1337); obj.save().then(function(obj) { @@ -121,10 +121,7 @@ describe('miscellaneous', function() { fail('one of the users should not have been created'); done(); }) - .catch(error => { - fail('index build failed') - done(); - }); + .catch(done); }); it('ensure that if people already have duplicate users, they can still sign up new users', done => { diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index deaec85f7b..ce0f2dd583 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -30,20 +30,11 @@ export class PostgresStorageAdapter { }; classExists(name) { - console.log('classExists(name) {') - return this.connect().then(() => { - return this.database.listCollections({ name: this._collectionPrefix + name }).toArray(); - }).then(collections => { - return collections.length > 0; - }); + return Promise.reject('Not implented yet.') } setClassLevelPermissions(className, CLPs) { - console.log('setClassLevelPermissions(className, CLPs) {') - return this._schemaCollection() - .then(schemaCollection => schemaCollection.updateSchema(className, { - $set: { _metadata: { class_permissions: CLPs } } - })); + return Promise.reject('Not implented yet.') } createClass(className, schema) { @@ -78,19 +69,7 @@ export class PostgresStorageAdapter { // Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.) // and resolves with false if it wasn't (eg. a join table). Rejects if deletion was impossible. deleteClass(className) { - console.log('deleteClass(className) {') - return this._adaptiveCollection(className) - .then(collection => collection.drop()) - .catch(error => { - // 'ns not found' means collection was already gone. Ignore deletion attempt. - if (error.message == 'ns not found') { - return; - } - throw error; - }) - // We've dropped the collection, now remove the _SCHEMA document - .then(() => this._schemaCollection()) - .then(schemaCollection => schemaCollection.findAndDeleteSchema(className)) + return Promise.reject('Not implented yet.') } // Delete all data known to this adatper. Used for testing. @@ -130,28 +109,7 @@ export class PostgresStorageAdapter { // Returns a Promise. deleteFields(className, schema, fieldNames) { - console.log('deleteFields(className, schema, fieldNames) {') - const mongoFormatNames = fieldNames.map(fieldName => { - if (schema.fields[fieldName].type === 'Pointer') { - return `_p_${fieldName}` - } else { - return fieldName; - } - }); - const collectionUpdate = { '$unset' : {} }; - mongoFormatNames.forEach(name => { - collectionUpdate['$unset'][name] = null; - }); - - const schemaUpdate = { '$unset' : {} }; - fieldNames.forEach(name => { - schemaUpdate['$unset'][name] = null; - }); - - return this._adaptiveCollection(className) - .then(collection => collection.updateMany({}, collectionUpdate)) - .then(() => this._schemaCollection()) - .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate)); + return Promise.reject('Not implented yet.') } // Return a promise for all schemas known to this adapter, in Parse format. In case the @@ -187,47 +145,22 @@ export class PostgresStorageAdapter { // If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined. // If there is some other error, reject with INTERNAL_SERVER_ERROR. deleteObjectsByQuery(className, schema, query) { - console.log('deleteObjectsByQuery(className, schema, query) {') - return this._adaptiveCollection(className) - .then(collection => { - let mongoWhere = transformWhere(className, query, schema); - return collection.deleteMany(mongoWhere) - }) - .then(({ result }) => { - if (result.n === 0) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); - } - return Promise.resolve(); - }, error => { - throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error'); - }); + return Promise.reject('Not implented yet.') } // Apply the update to all objects that match the given Parse Query. updateObjectsByQuery(className, schema, query, update) { - console.log('updateObjectsByQuery(className, schema, query, update) {') - const mongoUpdate = transformUpdate(className, update, schema); - const mongoWhere = transformWhere(className, query, schema); - return this._adaptiveCollection(className) - .then(collection => collection.updateMany(mongoWhere, mongoUpdate)); + return Promise.reject('Not implented yet.') } // Hopefully we can get rid of this in favor of updateObjectsByQuery. findOneAndUpdate(className, schema, query, update) { - console.log('findOneAndUpdate(className, schema, query, update) {') - const mongoUpdate = transformUpdate(className, update, schema); - const mongoWhere = transformWhere(className, query, schema); - return this._adaptiveCollection(className) - .then(collection => collection.findOneAndUpdate(mongoWhere, mongoUpdate)); + return Promise.reject('Not implented yet.') } // Hopefully we can get rid of this. It's only used for config and hooks. upsertOneObject(className, schema, query, update) { - console.log('upsertOneObject(className, schema, query, update) {') - const mongoUpdate = transformUpdate(className, update, schema); - const mongoWhere = transformWhere(className, query, schema); - return this._adaptiveCollection(className) - .then(collection => collection.upsertOne(mongoWhere, mongoUpdate)); + return Promise.reject('Not implented yet.') } // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. @@ -241,29 +174,12 @@ export class PostgresStorageAdapter { // Way of determining if a field is nullable. Undefined doesn't count against uniqueness, // which is why we use sparse indexes. ensureUniqueness(className, schema, fieldNames) { - console.log('ensureUniqueness(className, schema, fieldNames) {') - return Promise.resolve(); - let indexCreationRequest = {}; - let mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema)); - mongoFieldNames.forEach(fieldName => { - indexCreationRequest[fieldName] = 1; - }); - return this._adaptiveCollection(className) - .then(collection => collection._ensureSparseUniqueIndexInBackground(indexCreationRequest)) - .catch(error => { - if (error.code === 11000) { - throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Tried to ensure field uniqueness for a class that already has duplicates.'); - } else { - throw error; - } - }); + return Promise.resolve('ensureUniqueness not implented yet.') } // Executs a count. count(className, schema, query) { - console.log('count(className, schema, query) {') - return this._adaptiveCollection(className) - .then(collection => collection.count(transformWhere(className, query, schema))); + return Promise.reject('Not implented yet.') } } From ba6f828a2e8e1eb0ac5537692cc44356270efe67 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Sat, 11 Jun 2016 18:08:20 -0700 Subject: [PATCH 44/44] Fix test leaving behind connections --- spec/ParseAPI.spec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 63f5808574..a1fb544024 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -213,8 +213,7 @@ describe('miscellaneous', function() { expect(typeof user.id).toEqual('string'); expect(user.get('password')).toBeUndefined(); expect(user.getSessionToken()).not.toBeUndefined(); - Parse.User.logOut(); - done(); + Parse.User.logOut().then(done); }, error: function(error) { fail(error); }