Skip to content
Merged
2 changes: 1 addition & 1 deletion 2.3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,6 @@ coll.aggregate([
{$match: {count: {"$gt": 1}}},
{$project: {id: "$uniqueIds", username: "$_id", _id : 0} },
{$unwind: "$id" },
{$out: '_duplicates'} // Save the list of duplicates to a new, "_duplicates collection. Remove this line to just output the list.
{$out: '_duplicates'} // Save the list of duplicates to a new, "_duplicates" collection. Remove this line to just output the list.
], {allowDiskUse:true})
```
2 changes: 1 addition & 1 deletion spec/MongoStorageAdapter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('MongoStorageAdapter', () => {

it('stores objectId in _id', done => {
let adapter = new MongoStorageAdapter({ uri: databaseURI });
adapter.createObject('Foo', {}, { objectId: 'abcde' })
adapter.createObject('Foo', { fields: {} }, { objectId: 'abcde' })
.then(() => adapter._rawFind('Foo', {}))
.then(results => {
expect(results.length).toEqual(1);
Expand Down
26 changes: 14 additions & 12 deletions spec/ParseAPI.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ describe('miscellaneous', function() {
expect(obj2.id).toEqual(obj.id);
done();
},
error: fail
error: error => {
fail(JSON.stringify(error));
done();
}
});
});
});
Expand All @@ -48,8 +51,8 @@ describe('miscellaneous', function() {
expect(data.getSessionToken()).not.toBeUndefined();
expect(data.get('password')).toBeUndefined();
done();
}, function(err) {
fail(err);
}, error => {
fail(JSON.stringify(error));
done();
});
});
Expand Down Expand Up @@ -86,9 +89,8 @@ describe('miscellaneous', function() {
});

it('ensure that email is uniquely indexed', done => {
let numCreated = 0;
let numFailed = 0;

let numCreated = 0;
let user1 = new Parse.User();
user1.setPassword('asdf');
user1.setUsername('u1');
Expand Down Expand Up @@ -215,8 +217,9 @@ describe('miscellaneous', function() {
expect(user.get('password')).toBeUndefined();
expect(user.getSessionToken()).not.toBeUndefined();
Parse.User.logOut().then(done);
}, error: function(error) {
fail(error);
}, error: error => {
fail(JSON.stringify(error));
done();
}
});
}, fail);
Expand All @@ -232,15 +235,14 @@ describe('miscellaneous', function() {
expect(user.get('foo')).toEqual(1);
user.increment('foo');
return user.save();
}).then(() => {
Parse.User.logOut();
return Parse.User.logIn('test', 'moon-y');
}).then((user) => {
}).then(() => Parse.User.logOut())
.then(() => Parse.User.logIn('test', 'moon-y'))
.then((user) => {
expect(user.get('foo')).toEqual(2);
Parse.User.logOut()
.then(done);
}, (error) => {
fail(error);
fail(JSON.stringify(error));
done();
});
});
Expand Down
6 changes: 3 additions & 3 deletions spec/ParseInstallation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('Installations', () => {
'deviceType': device
};
rest.create(config, auth.nobody(config), '_Installation', input)
.then(() => config.database.adapter.find('_Installation', installationSchema, {}, {}))
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
.then(results => {
expect(results.length).toEqual(1);
var obj = results[0];
Expand All @@ -42,7 +42,7 @@ describe('Installations', () => {
'deviceType': device
};
rest.create(config, auth.nobody(config), '_Installation', input)
.then(() => config.database.adapter.find('_Installation', installationSchema, {}, {}))
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
.then(results => {
expect(results.length).toEqual(1);
var obj = results[0];
Expand All @@ -60,7 +60,7 @@ describe('Installations', () => {
'deviceType': device
};
rest.create(config, auth.nobody(config), '_Installation', input)
.then(() => config.database.adapter.find('_Installation', installationSchema, {}, {}))
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
.then(results => {
expect(results.length).toEqual(1);
var obj = results[0];
Expand Down
76 changes: 39 additions & 37 deletions spec/ParseUser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
var request = require('request');
var passwordCrypto = require('../src/password');
var Config = require('../src/Config');
const rp = require('request-promise');

function verifyACL(user) {
const ACL = user.getACL();
Expand Down Expand Up @@ -2131,7 +2132,7 @@ describe('Parse.User testing', () => {
let database = new Config(Parse.applicationId).database;
database.create('_User', {
username: 'user',
password: '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie',
_hashed_password: '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie',
_auth_data_facebook: null
}, {}).then(() => {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -2258,42 +2259,43 @@ describe('Parse.User testing', () => {
});

it('should fail to become user with expired token', (done) => {
Parse.User.signUp("auser", "somepass", null, {
success: function(user) {
request.get({
url: 'http://localhost:8378/1/classes/_Session',
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
},
}, (error, response, body) => {
var id = body.results[0].objectId;
var expiresAt = new Date((new Date()).setYear(2015));
var token = body.results[0].sessionToken;
request.put({
url: "http://localhost:8378/1/classes/_Session/" + id,
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
},
body: {
expiresAt: { __type: "Date", iso: expiresAt.toISOString() },
},
}, (error, response, body) => {
Parse.User.become(token)
.then(() => { fail("Should not have succeded"); })
.fail((err) => {
expect(err.code).toEqual(209);
expect(err.message).toEqual("Session token is expired.");
Parse.User.logOut() // Logout to prevent polluting CLI with messages
.then(done());
});
});
});
}
});
let token;
Parse.User.signUp("auser", "somepass", null)
.then(user => rp({
method: 'GET',
url: 'http://localhost:8378/1/classes/_Session',
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
},
}))
.then(body => {
var id = body.results[0].objectId;
var expiresAt = new Date((new Date()).setYear(2015));
token = body.results[0].sessionToken;
return rp({
method: 'PUT',
url: "http://localhost:8378/1/classes/_Session/" + id,
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
},
body: {
expiresAt: { __type: "Date", iso: expiresAt.toISOString() },
},
})
})
.then(() => Parse.User.become(token))
.then(() => {
fail("Should not have succeded")
done();
}, error => {
expect(error.code).toEqual(209);
expect(error.message).toEqual("Session token is expired.");
done();
})
});

it('should not create extraneous session tokens', (done) => {
Expand Down
4 changes: 2 additions & 2 deletions spec/PointerPermissions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ describe('Pointer Permissions', () => {
expect(res.length).toBe(1);
expect(res[0].id).toBe(obj.id);
done();
}).catch((err) => {
fail('Should not fail');
}).catch(error => {
fail(JSON.stringify(error));
done();
});
});
Expand Down
2 changes: 1 addition & 1 deletion spec/Schema.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ describe('SchemaController', () => {
objectId: { type: 'String' },
updatedAt: { type: 'Date' },
createdAt: { type: 'Date' },
ACL: { type: 'ACL' }
ACL: { type: 'ACL' },
};
expect(dd(schema.data.NewClass, expectedSchema)).toEqual(undefined);
done();
Expand Down
19 changes: 7 additions & 12 deletions spec/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ 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;
const PostgresStorageAdapter = require('../src/Adapters/Storage/Postgres/PostgresStorageAdapter');

Expand All @@ -30,7 +29,6 @@ if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
})
}


var port = 8378;

let gridStoreAdapter = new GridStoreAdapter(mongoURI);
Expand Down Expand Up @@ -142,6 +140,12 @@ beforeEach(done => {
});

afterEach(function(done) {
let afterLogOut = () => {
if (Object.keys(openConnections).length > 0) {
fail('There were open connections to the server left after the test finished');
}
done();
};
Parse.Cloud._removeAllHooks();
databaseAdapter.getAllClasses()
.then(allSchemas => {
Expand All @@ -159,16 +163,7 @@ afterEach(function(done) {
});
})
.then(() => Parse.User.logOut())
.then(() => {
if (Object.keys(openConnections).length > 0) {
fail('There were open connections to the server left after the test finished');
}
done();
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
.then(afterLogOut, afterLogOut)
});

var TestObject = Parse.Object.extend({
Expand Down
2 changes: 1 addition & 1 deletion src/Adapters/Storage/Mongo/MongoCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export default class MongoCollection {
// If there is nothing that matches the query - does insert
// Postgres Note: `INSERT ... ON CONFLICT UPDATE` that is available since 9.5.
upsertOne(query, update) {
return this._mongoCollection.update(query, update, { upsert: true });
return this._mongoCollection.update(query, update, { upsert: true })
}

updateOne(query, update) {
Expand Down
1 change: 0 additions & 1 deletion src/Adapters/Storage/Mongo/MongoSchemaCollection.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import MongoCollection from './MongoCollection';

function mongoFieldToParseSchemaField(type) {
Expand Down
30 changes: 27 additions & 3 deletions src/Adapters/Storage/Mongo/MongoStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ const storageAdapterAllCollections = mongoAdapter => {
});
}

const convertParseSchemaToMongoSchema = ({...schema}) => {
delete schema.fields._rperm;
delete schema.fields._wperm;

if (schema.className === '_User') {
// Legacy mongo adapter knows about the difference between password and _hashed_password.
// Future database adapters will only know about _hashed_password.
// Note: Parse Server will bring back password with injectDefaultSchema, so we don't need
// to add _hashed_password back ever.
delete schema.fields._hashed_password;
}

return schema;
}

export class MongoStorageAdapter {
// Private
_uri: string;
Expand Down Expand Up @@ -97,6 +112,7 @@ export class MongoStorageAdapter {
}

createClass(className, schema) {
schema = convertParseSchemaToMongoSchema(schema);
return this._schemaCollection()
.then(schemaCollection => schemaCollection.addSchema(className, schema.fields, schema.classLevelPermissions));
}
Expand Down Expand Up @@ -185,13 +201,14 @@ export class MongoStorageAdapter {
// undefined as the reason.
getClass(className) {
return this._schemaCollection()
.then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className));
.then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className))
}

// 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, schema, object) {
schema = convertParseSchemaToMongoSchema(schema);
const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema);
return this._adaptiveCollection(className)
.then(collection => collection.insertOne(mongoObject))
Expand All @@ -208,6 +225,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, schema, query) {
schema = convertParseSchemaToMongoSchema(schema);
return this._adaptiveCollection(className)
.then(collection => {
let mongoWhere = transformWhere(className, query, schema);
Expand All @@ -225,15 +243,17 @@ export class MongoStorageAdapter {

// Apply the update to all objects that match the given Parse Query.
updateObjectsByQuery(className, schema, query, update) {
schema = convertParseSchemaToMongoSchema(schema);
const mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className)
.then(collection => collection.updateMany(mongoWhere, mongoUpdate));
}

// Atomically finds and updates an object based on query.
// Resolve with the updated object.
// Return value not currently well specified.
findOneAndUpdate(className, schema, query, update) {
schema = convertParseSchemaToMongoSchema(schema);
const mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className)
Expand All @@ -243,6 +263,7 @@ export class MongoStorageAdapter {

// Hopefully we can get rid of this. It's only used for config and hooks.
upsertOneObject(className, schema, query, update) {
schema = convertParseSchemaToMongoSchema(schema);
const mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className)
Expand All @@ -251,11 +272,12 @@ export class MongoStorageAdapter {

// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
find(className, schema, query, { skip, limit, sort }) {
schema = convertParseSchemaToMongoSchema(schema);
let mongoWhere = transformWhere(className, query, schema);
let mongoSort = _.mapKeys(sort, (value, fieldName) => transformKey(className, fieldName, schema));
return this._adaptiveCollection(className)
.then(collection => collection.find(mongoWhere, { skip, limit, sort: mongoSort }))
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)));
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)))
}

// Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't
Expand All @@ -264,6 +286,7 @@ export class MongoStorageAdapter {
// 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) {
schema = convertParseSchemaToMongoSchema(schema);
let indexCreationRequest = {};
let mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema));
mongoFieldNames.forEach(fieldName => {
Expand All @@ -287,6 +310,7 @@ export class MongoStorageAdapter {

// Executs a count.
count(className, schema, query) {
schema = convertParseSchemaToMongoSchema(schema);
return this._adaptiveCollection(className)
.then(collection => collection.count(transformWhere(className, query, schema)));
}
Expand Down
Loading