From cd525802a63538b48edf6320229017dc3eac4d4e Mon Sep 17 00:00:00 2001 From: Drew Date: Sat, 28 May 2016 09:25:09 -0700 Subject: [PATCH] Remove mongoFind and mostly remove adaptiveCollection (#1924) * Use adapter.count * use adapter.upsertOneObject * Use adapter.deleteObjectsByQuery * Use adapter.find * use adapter.find * Update tests to avoid mongoFind * Fix a test to not use mongoFind * Fix a test to not use mongoFind * remove some mongoFind * Remove some mongoFind * Remove some mongoFind * Remove more mongoFind * remove more mongoFind * remove more mongoFind * remove more mongoFind * remove more mongoFind * remove more mongoFind * remove more mongoFind * remove more mongoFind * remove more mongoFind * Restore update ios device token with duplicate device token to original * remove a mongoFind * remove a mongoFind * formatting * formatting * remove a mongoFind * remove a mongoFind * remove a mongoFind * kill mongoFind * Fix tests * Fix tests * fix syntax * Fix test --- spec/MongoStorageAdapter.spec.js | 115 ++++++ spec/OAuth.spec.js | 8 +- spec/ParseAPI.spec.js | 2 +- spec/ParseInstallation.spec.js | 333 ++++++++---------- spec/RestCreate.spec.js | 107 +++--- .../Storage/Mongo/MongoStorageAdapter.js | 5 + src/Controllers/DatabaseController.js | 38 +- 7 files changed, 349 insertions(+), 259 deletions(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index daabd221..fad763ca 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -2,8 +2,17 @@ const MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter'); const MongoClient = require('mongodb').MongoClient; +const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; +// These tests are specific to the mongo storage adapter + mongo storage format +// and will eventually be moved into their own repo describe('MongoStorageAdapter', () => { + beforeEach(done => { + new MongoStorageAdapter({ uri: databaseURI }) + .deleteAllSchemas() + .then(done, fail); + }); + it('auto-escapes symbols in auth information', () => { spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null)); new MongoStorageAdapter({ @@ -37,4 +46,110 @@ describe('MongoStorageAdapter', () => { jasmine.any(Object) ); }); + + it('stores objectId in _id', done => { + let adapter = new MongoStorageAdapter({ uri: databaseURI }); + adapter.createObject('Foo', { objectId: 'abcde' }, { fields: { objectId: 'String' } }) + .then(() => adapter._rawFind('Foo', {})) + .then(results => { + expect(results.length).toEqual(1); + var obj = results[0]; + expect(typeof obj._id).toEqual('string'); + expect(obj.objectId).toBeUndefined(); + done(); + }); + }); + + it('stores pointers with a _p_ prefix', (done) => { + let obj = { + objectId: 'bar', + aPointer: { + __type: 'Pointer', + className: 'JustThePointer', + objectId: 'qwerty' + } + }; + let adapter = new MongoStorageAdapter({ uri: databaseURI }); + adapter.createObject('APointerDarkly', obj, { fields: { + objectId: { type: 'String' }, + aPointer: { type: 'Pointer', targetClass: 'JustThePointer' }, + }}) + .then(() => adapter._rawFind('APointerDarkly', {})) + .then(results => { + expect(results.length).toEqual(1); + let output = results[0]; + expect(typeof output._id).toEqual('string'); + expect(typeof output._p_aPointer).toEqual('string'); + expect(output._p_aPointer).toEqual('JustThePointer$qwerty'); + expect(output.aPointer).toBeUndefined(); + done(); + }); + }); + + it('handles object and subdocument', done => { + let adapter = new MongoStorageAdapter({ uri: databaseURI }); + let schema = { fields : { subdoc: { type: 'Object' } } }; + let obj = { subdoc: {foo: 'bar', wu: 'tan'} }; + adapter.createObject('MyClass', obj, schema) + .then(() => adapter._rawFind('MyClass', {})) + .then(results => { + expect(results.length).toEqual(1); + let mob = results[0]; + expect(typeof mob.subdoc).toBe('object'); + expect(mob.subdoc.foo).toBe('bar'); + expect(mob.subdoc.wu).toBe('tan'); + let obj = { 'subdoc.wu': 'clan' }; + return adapter.findOneAndUpdate('MyClass', {}, schema, obj); + }) + .then(() => adapter._rawFind('MyClass', {})) + .then(results => { + expect(results.length).toEqual(1); + let mob = results[0]; + expect(typeof mob.subdoc).toBe('object'); + expect(mob.subdoc.foo).toBe('bar'); + expect(mob.subdoc.wu).toBe('clan'); + done(); + }); + }); + + it('handles array, object, date', (done) => { + let adapter = new MongoStorageAdapter({ uri: databaseURI }); + let obj = { + array: [1, 2, 3], + object: {foo: 'bar'}, + date: { + __type: 'Date', + iso: '2016-05-26T20:55:01.154Z', + }, + }; + let schema = { fields: { + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, + } }; + adapter.createObject('MyClass', obj, schema) + .then(() => adapter._rawFind('MyClass', {})) + .then(results => { + expect(results.length).toEqual(1); + let mob = results[0]; + 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, {}); + }) + .then(results => { + expect(results.length).toEqual(1); + let mob = results[0]; + expect(mob.array instanceof Array).toBe(true); + expect(typeof mob.object).toBe('object'); + expect(mob.date.__type).toBe('Date'); + expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z'); + done(); + }) + .catch(error => { + console.log(error); + fail(); + done(); + }); + }); }); diff --git a/spec/OAuth.spec.js b/spec/OAuth.spec.js index 0a50611c..ee505151 100644 --- a/spec/OAuth.spec.js +++ b/spec/OAuth.spec.js @@ -1,6 +1,7 @@ var OAuth = require("../src/authDataManager/OAuth1Client"); var request = require('request'); var Config = require("../src/Config"); +var defaultColumns = require('../src/Controllers/SchemaController').defaultColumns; describe('OAuth', function() { @@ -283,9 +284,10 @@ describe('OAuth', function() { "Expiration should be cleared"); // make sure the auth data is properly deleted var config = new Config(Parse.applicationId); - config.database.mongoFind('_User', { - _id: model.id - }).then((res) => { + config.database.adapter.find('_User', { objectId: model.id }, { + fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation), + }, {}) + .then(res => { expect(res.length).toBe(1); expect(res[0]._auth_data_myoauth).toBeUndefined(); expect(res[0]._auth_data_myoauth).not.toBeNull(); diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 688569e6..471bbea4 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -203,7 +203,7 @@ describe('miscellaneous', function() { return obj.save(); }).then(() => { var db = DatabaseAdapter.getDatabaseConnection(appId, 'test_'); - return db.mongoFind('TestObject', {}, {}); + return db.adapter.find('TestObject', {}, { fields: {} }, {}); }).then((results) => { expect(results.length).toEqual(1); expect(results[0]['foo']).toEqual('bar'); diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index 7c255a6c..f00c5be1 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -11,9 +11,11 @@ var rest = require('../src/rest'); var config = new Config('test'); let database = DatabaseAdapter.getDatabaseConnection('test', 'test_'); +let defaultColumns = require('../src/Controllers/SchemaController').defaultColumns; + +const installationSchema = { fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation) }; describe('Installations', () => { - it('creates an android installation with ids', (done) => { var installId = '12345678-abcd-abcd-abcd-123456789abc'; var device = 'android'; @@ -22,9 +24,8 @@ describe('Installations', () => { 'deviceType': device }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); var obj = results[0]; expect(obj.installationId).toEqual(installId); @@ -41,9 +42,8 @@ describe('Installations', () => { 'deviceType': device }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); var obj = results[0]; expect(obj.deviceToken).toEqual(t); @@ -60,9 +60,8 @@ describe('Installations', () => { 'deviceType': device }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); var obj = results[0]; expect(obj.installationId).toEqual(installId); @@ -80,9 +79,8 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); var obj = results[0]; expect(obj.installationId).toEqual(installId); @@ -104,9 +102,8 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); var obj = results[0]; expect(obj.deviceToken).toEqual(t); @@ -203,9 +200,8 @@ describe('Installations', () => { 'custom': 'allowed' }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); var obj = results[0]; expect(obj.custom).toEqual('allowed'); @@ -228,18 +224,17 @@ describe('Installations', () => { var firstObject; var secondObject; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); firstObject = results[0]; delete input.deviceToken; delete input.channels; input['foo'] = 'bar'; return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); secondObject = results[0]; expect(firstObject._id).toEqual(secondObject._id); @@ -268,15 +263,14 @@ describe('Installations', () => { var firstObject; var secondObject; rest.create(config, auth.nobody(config), '_Installation', input1) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .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(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(2); if (results[0]['_id'] == firstObject._id) { secondObject = results[1]; @@ -284,9 +278,9 @@ describe('Installations', () => { secondObject = results[0]; } return rest.create(config, auth.nobody(config), '_Installation', input3); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0]['_id']).toEqual(secondObject._id); done(); @@ -310,17 +304,14 @@ describe('Installations', () => { }).then(() => { input.installationId = installId3; return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', - {installationId: installId1}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', {installationId: installId1}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); - return database.mongoFind('_Installation', - {installationId: installId2}, {}); - }).then((results) => { + return database.adapter.find('_Installation', {installationId: installId2}, installationSchema, {}); + }).then(results => { expect(results.length).toEqual(1); - return database.mongoFind('_Installation', - {installationId: installId3}, {}); + return database.adapter.find('_Installation', {installationId: installId3}, installationSchema, {}); }).then((results) => { expect(results.length).toEqual(1); done(); @@ -329,29 +320,31 @@ describe('Installations', () => { it('updating with new channels', (done) => { var input = { - 'installationId': '12345678-abcd-abcd-abcd-123456789abc', - 'deviceType': 'android', - 'channels': ['foo', 'bar'] + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', + channels: ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); - var id = results[0]['_id']; + var id = results[0].objectId; var update = { 'channels': ['baz'] }; - return rest.update(config, auth.nobody(config), - '_Installation', id, update); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + return rest.update(config, auth.nobody(config), '_Installation', id, update); + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0].channels.length).toEqual(1); expect(results[0].channels[0]).toEqual('baz'); done(); - }).catch((error) => { console.log(error); }); + }).catch(error => { + console.log(error); + fail(); + done(); + }); }); it('update android fails with new installation id', (done) => { @@ -363,15 +356,11 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); - input = { - 'installationId': installId2 - }; - return rest.update(config, auth.nobody(config), '_Installation', - results[0]['_id'], input); + input = { 'installationId': installId2 }; + return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); }).then(() => { fail('Updating the installation should have failed.'); done(); @@ -390,15 +379,11 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); - input = { - 'deviceToken': b - }; - return rest.update(config, auth.nobody(config), '_Installation', - results[0]['_id'], input); + input = { 'deviceToken': b }; + return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); }).then(() => { fail('Updating the installation should have failed.'); }).catch((error) => { @@ -418,20 +403,18 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); input = { 'installationId': installId, 'deviceToken': u, 'deviceType': 'ios' }; - return rest.update(config, auth.nobody(config), '_Installation', - results[0]['_id'], input); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0].deviceToken).toEqual(u); done(); @@ -446,15 +429,13 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); input = { 'deviceType': 'ios' }; - return rest.update(config, auth.nobody(config), '_Installation', - results[0]['_id'], input); + return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); }).then(() => { fail('Should not have been able to update Installation.'); done(); @@ -472,18 +453,16 @@ describe('Installations', () => { 'channels': ['foo', 'bar'] }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); input = { 'custom': 'allowed' }; - return rest.update(config, auth.nobody(config), '_Installation', - results[0]['_id'], input); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0]['custom']).toEqual('allowed'); done(); @@ -502,40 +481,36 @@ describe('Installations', () => { var firstObject; var secondObject; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - input = { - 'installationId': installId2, - 'deviceType': 'android' - }; - return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', - {installationId: installId1}, {}); - }).then((results) => { - expect(results.length).toEqual(1); + .then(() => { + input = { + 'installationId': installId2, + 'deviceType': 'android' + }; + return rest.create(config, auth.nobody(config), '_Installation', input); + }) + .then(() => database.adapter.find('_Installation', {installationId: installId1}, installationSchema, {})) + .then(results => { firstObject = results[0]; - return database.mongoFind('_Installation', - {installationId: installId2}, {}); - }).then((results) => { + expect(results.length).toEqual(1); + return database.adapter.find('_Installation', {installationId: installId2}, installationSchema, {}); + }).then(results => { expect(results.length).toEqual(1); secondObject = results[0]; // Update second installation to conflict with first installation input = { - 'objectId': secondObject._id, + 'objectId': secondObject.objectId, 'deviceToken': t }; - return rest.update(config, auth.nobody(config), '_Installation', - secondObject._id, input); - }).then(() => { + return rest.update(config, auth.nobody(config), '_Installation', secondObject.objectId, input); + }) + .then(() => database.adapter.find('_Installation', {objectId: firstObject.objectId}, installationSchema, {})) + .then(results => { // The first object should have been deleted - return database.mongoFind('_Installation', {_id: firstObject._id}, {}); - }).then((results) => { expect(results.length).toEqual(0); done(); }).catch((error) => { console.log(error); }); }); - it('update ios device token with duplicate device token', (done) => { var installId1 = '11111111-abcd-abcd-abcd-123456789abc'; var installId2 = '22222222-abcd-abcd-abcd-123456789abc'; @@ -554,15 +529,14 @@ describe('Installations', () => { 'deviceType': 'ios' }; return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', - {installationId: installId1}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', {installationId: installId1}, installationSchema, {})) + .then((results) => { expect(results.length).toEqual(1); firstObject = results[0]; - return database.mongoFind('_Installation', - {installationId: installId2}, {}); - }).then((results) => { + return database.adapter.find('_Installation', {installationId: installId2}, installationSchema, {}); + }) + .then(results => { expect(results.length).toEqual(1); secondObject = results[0]; // Update second installation to conflict with first installation id @@ -570,12 +544,11 @@ describe('Installations', () => { 'installationId': installId2, 'deviceToken': t }; - return rest.update(config, auth.nobody(config), '_Installation', - secondObject._id, input); - }).then(() => { + return rest.update(config, auth.nobody(config), '_Installation', secondObject.objectId, input); + }) + .then(() => database.adapter.find('_Installation', {objectId: firstObject.objectId}, installationSchema, {})) + .then(results => { // The first object should have been deleted - return database.mongoFind('_Installation', {_id: firstObject._id}, {}); - }).then((results) => { expect(results.length).toEqual(0); done(); }).catch((error) => { console.log(error); }); @@ -596,9 +569,9 @@ describe('Installations', () => { input.installationId = installId2; input.appIdentifier = 'bar'; return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { // The first object should have been deleted during merge expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId2); @@ -614,19 +587,17 @@ describe('Installations', () => { 'deviceType': 'ios' }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); input = { 'deviceToken': t, 'channels': [] }; - return rest.update(config, auth.nobody(config), '_Installation', - results[0]['_id'], input); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); expect(results[0].deviceToken).toEqual(t); @@ -649,21 +620,19 @@ describe('Installations', () => { 'deviceType': 'ios' }; return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', - {deviceToken: t}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', { deviceToken: t }, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); input = { 'deviceToken': t, 'installationId': installId, 'deviceType': 'ios' }; - return rest.update(config, auth.nobody(config), '_Installation', - results[0]['_id'], input); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); expect(results[0].deviceToken).toEqual(t); @@ -686,10 +655,9 @@ describe('Installations', () => { 'deviceType': 'ios' }; return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', - {deviceToken: t}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', { deviceToken: t }, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); input = { 'deviceToken': t, @@ -700,11 +668,10 @@ describe('Installations', () => { 'amount': 1 } }; - return rest.update(config, auth.nobody(config), '_Installation', - results[0]['_id'], input); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); expect(results[0].deviceToken).toEqual(t); @@ -724,9 +691,8 @@ describe('Installations', () => { var installObj; var tokenObj; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); installObj = results[0]; input = { @@ -734,9 +700,9 @@ describe('Installations', () => { 'deviceType': 'ios' }; return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', {deviceToken: t}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', { deviceToken: t }, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); tokenObj = results[0]; input = { @@ -744,11 +710,10 @@ describe('Installations', () => { 'deviceToken': t, 'deviceType': 'ios' }; - return rest.update(config, auth.nobody(config), '_Installation', - installObj._id, input); - }).then(() => { - return database.mongoFind('_Installation', {_id: tokenObj._id}, {}); - }).then((results) => { + return rest.update(config, auth.nobody(config), '_Installation', installObj.objectId, input); + }) + .then(() => database.adapter.find('_Installation', { objectId: tokenObj.objectId }, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); expect(results[0].deviceToken).toEqual(t); @@ -766,9 +731,8 @@ describe('Installations', () => { var installObj; var tokenObj; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); installObj = results[0]; input = { @@ -776,9 +740,9 @@ describe('Installations', () => { 'deviceType': 'ios' }; return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', {deviceToken: t}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', { deviceToken: t }, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); tokenObj = results[0]; input = { @@ -790,11 +754,10 @@ describe('Installations', () => { 'amount': 1 } }; - return rest.update(config, auth.nobody(config), '_Installation', - installObj._id, input); - }).then(() => { - return database.mongoFind('_Installation', {_id: tokenObj._id}, {}); - }).then((results) => { + return rest.update(config, auth.nobody(config), '_Installation', installObj.objectId, input); + }) + .then(() => database.adapter.find('_Installation', { objectId: tokenObj.objectId }, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); expect(results[0].deviceToken).toEqual(t); @@ -821,9 +784,8 @@ describe('Installations', () => { 'deviceType': 'ios' }; rest.create(config, auth.nobody(config), '_Installation', input) - .then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); input = { 'installationId': installId, @@ -831,13 +793,18 @@ describe('Installations', () => { 'deviceType': 'ios' }; return rest.create(config, auth.nobody(config), '_Installation', input); - }).then(() => { - return database.mongoFind('_Installation', {}, {}); - }).then((results) => { + }) + .then(() => database.adapter.find('_Installation', {}, installationSchema, {})) + .then(results => { expect(results.length).toEqual(1); expect(results[0].deviceToken).toEqual(t); expect(results[0].installationId).toEqual(installId); done(); + }) + .catch(error => { + console.log(error); + fail(); + done(); }); }); diff --git a/spec/RestCreate.spec.js b/spec/RestCreate.spec.js index 13efc95f..226121b3 100644 --- a/spec/RestCreate.spec.js +++ b/spec/RestCreate.spec.js @@ -1,3 +1,4 @@ +"use strict"; // These tests check the "create" / "update" functionality of the REST API. var auth = require('../src/Auth'); var cache = require('../src/cache'); @@ -11,65 +12,69 @@ var config = new Config('test'); var database = DatabaseAdapter.getDatabaseConnection('test', 'test_'); describe('rest create', () => { - it('handles _id', (done) => { - rest.create(config, auth.nobody(config), 'Foo', {}).then(() => { - return database.mongoFind('Foo', {}); - }).then((results) => { + it('handles _id', done => { + rest.create(config, auth.nobody(config), 'Foo', {}) + .then(() => database.adapter.find('Foo', {}, { fields: {} }, {})) + .then(results => { expect(results.length).toEqual(1); var obj = results[0]; - expect(typeof obj._id).toEqual('string'); - expect(obj.objectId).toBeUndefined(); + expect(typeof obj.objectId).toEqual('string'); + expect(obj._id).toBeUndefined(); done(); }); }); it('handles array, object, date', (done) => { + let now = new Date(); var obj = { array: [1, 2, 3], object: {foo: 'bar'}, - date: Parse._encode(new Date()), + date: Parse._encode(now), }; - rest.create(config, auth.nobody(config), 'MyClass', obj).then(() => { - return database.mongoFind('MyClass', {}, {}); - }).then((results) => { + rest.create(config, auth.nobody(config), 'MyClass', obj) + .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]; expect(mob.array instanceof Array).toBe(true); expect(typeof mob.object).toBe('object'); - expect(mob.date instanceof Date).toBe(true); + expect(mob.date.__type).toBe('Date'); + expect(new Date(mob.date.iso).getTime()).toBe(now.getTime()); done(); }); }); - it('handles object and subdocument', (done) => { - var obj = { - subdoc: {foo: 'bar', wu: 'tan'}, - }; - rest.create(config, auth.nobody(config), 'MyClass', obj).then(() => { - return database.mongoFind('MyClass', {}, {}); - }).then((results) => { + 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(results => { expect(results.length).toEqual(1); - var mob = results[0]; + let mob = results[0]; expect(typeof mob.subdoc).toBe('object'); expect(mob.subdoc.foo).toBe('bar'); expect(mob.subdoc.wu).toBe('tan'); - expect(typeof mob._id).toEqual('string'); - - var obj = { - 'subdoc.wu': 'clan', - }; - - rest.update(config, auth.nobody(config), 'MyClass', mob._id, obj).then(() => { - return database.mongoFind('MyClass', {}, {}); - }).then((results) => { - expect(results.length).toEqual(1); - var mob = results[0]; - expect(typeof mob.subdoc).toBe('object'); - expect(mob.subdoc.foo).toBe('bar'); - expect(mob.subdoc.wu).toBe('clan'); - done(); - }); - + expect(typeof mob.objectId).toEqual('string'); + let obj = { 'subdoc.wu': 'clan' }; + return rest.update(config, auth.nobody(config), 'MyClass', mob.objectId, obj) + }) + .then(() => database.adapter.find('MyClass', {}, { fields: {} }, {})) + .then(results => { + expect(results.length).toEqual(1); + let mob = results[0]; + expect(typeof mob.subdoc).toBe('object'); + expect(mob.subdoc.foo).toBe('bar'); + expect(mob.subdoc.wu).toBe('clan'); + done(); + }) + .catch(error => { + console.log(error); + fail(); + done(); }); }); @@ -240,8 +245,8 @@ describe('rest create', () => { }); }); - it('stores pointers with a _p_ prefix', (done) => { - var obj = { + it('stores pointers', done => { + let obj = { foo: 'bar', aPointer: { __type: 'Pointer', @@ -250,17 +255,23 @@ describe('rest create', () => { } }; rest.create(config, auth.nobody(config), 'APointerDarkly', obj) - .then((r) => { - return database.mongoFind('APointerDarkly', {}); - }).then((results) => { - expect(results.length).toEqual(1); - var output = results[0]; - expect(typeof output._id).toEqual('string'); - expect(typeof output._p_aPointer).toEqual('string'); - expect(output._p_aPointer).toEqual('JustThePointer$qwerty'); - expect(output.aPointer).toBeUndefined(); - done(); + .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]; + expect(typeof output.foo).toEqual('string'); + expect(typeof output._p_aPointer).toEqual('undefined'); + expect(output._p_aPointer).toBeUndefined(); + expect(output.aPointer).toEqual({ + __type: 'Pointer', + className: 'JustThePointer', + objectId: 'qwerty' }); + done(); + }); }); it("cannot set objectId", (done) => { diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index e62e9e30..8c95c338 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -236,6 +236,11 @@ export class MongoStorageAdapter { .then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema))); } + // Used in tests + _rawFind(className, query) { + return this.adaptiveCollection(className).then(collection => collection.find(query)); + } + // Executs a count. count(className, query, schema) { return this.adaptiveCollection(className) diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 8fbf45dc..b3344fee 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -305,15 +305,13 @@ DatabaseController.prototype.handleRelationUpdates = function(className, objectI // Adds a relation. // Returns a promise that resolves successfully iff the add was successful. +const relationSchema = { fields: { relatedId: { type: 'String' }, owningId: { type: 'String' } } }; DatabaseController.prototype.addRelation = function(key, fromClassName, fromId, toId) { let doc = { relatedId: toId, owningId : fromId }; - let className = `_Join:${key}:${fromClassName}`; - return this.adapter.adaptiveCollection(className).then((coll) => { - return coll.upsertOne(doc, doc); - }); + return this.adapter.upsertOneObject(`_Join:${key}:${fromClassName}`, doc, relationSchema, doc); }; // Removes a relation. @@ -324,9 +322,13 @@ DatabaseController.prototype.removeRelation = function(key, fromClassName, fromI relatedId: toId, owningId: fromId }; - let className = `_Join:${key}:${fromClassName}`; - return this.adapter.adaptiveCollection(className).then(coll => { - return coll.deleteOne(doc); + return this.adapter.deleteObjectsByQuery(`_Join:${key}:${fromClassName}`, doc, relationSchema) + .catch(error => { + // We don't care if they try to delete a non-existent relation. + if (error.code == Parse.Error.OBJECT_NOT_FOUND) { + return; + } + throw error; }); }; @@ -415,15 +417,6 @@ DatabaseController.prototype.canAddField = function(schema, className, object, a return Promise.resolve(); } -// Runs a mongo query on the database. -// This should only be used for testing - use 'find' for normal code -// to avoid Mongo-format dependencies. -// Returns a promise that resolves to a list of items. -DatabaseController.prototype.mongoFind = function(className, query, options = {}) { - return this.adapter.adaptiveCollection(className) - .then(collection => collection.find(query, options)); -}; - // Deletes everything in the database matching the current collectionPrefix // Won't delete collections in the system namespace // Returns a promise. @@ -449,17 +442,15 @@ 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.adaptiveCollection(joinTableName(className, key)) - .then(coll => coll.find({owningId : owningId})) - .then(results => results.map(r => r.relatedId)); + return this.adapter.find(joinTableName(className, key), { owningId }, relationSchema, {}) + .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.adaptiveCollection(joinTableName(className, key)) - .then(coll => coll.find({ relatedId: { '$in': relatedIds } })) - .then(results => results.map(r => r.owningId)); + return this.adapter.find(joinTableName(className, key), { relatedId: { '$in': relatedIds } }, relationSchema, {}) + .then(results => results.map(result => result.owningId)); }; // Modifies query so that it no longer has $in on relation fields, or @@ -702,8 +693,7 @@ DatabaseController.prototype.deleteSchema = function(className) { if (!exist) { return Promise.resolve(); } - return this.adapter.adaptiveCollection(className) - .then(collection => collection.count()) + 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.`);