Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Continue with postgres support #2053

Merged
merged 13 commits into from
Jun 16, 2016
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