Fix some stuff

This commit is contained in:
Drew Gross
2016-06-13 12:57:20 -07:00
parent f796d69d19
commit a69a88f3a4
9 changed files with 86 additions and 62 deletions

View File

@@ -49,7 +49,7 @@ describe('MongoStorageAdapter', () => {
it('stores objectId in _id', done => { it('stores objectId in _id', done => {
let adapter = new MongoStorageAdapter({ uri: databaseURI }); let adapter = new MongoStorageAdapter({ uri: databaseURI });
adapter.createObject('Foo', {}, { objectId: 'abcde' }) adapter.createObject('Foo', { fields: {} }, { objectId: 'abcde' })
.then(() => adapter._rawFind('Foo', {})) .then(() => adapter._rawFind('Foo', {}))
.then(results => { .then(results => {
expect(results.length).toEqual(1); expect(results.length).toEqual(1);

View File

@@ -10,6 +10,7 @@
var request = require('request'); var request = require('request');
var passwordCrypto = require('../src/password'); var passwordCrypto = require('../src/password');
var Config = require('../src/Config'); var Config = require('../src/Config');
const rp = require('request-promise');
function verifyACL(user) { function verifyACL(user) {
const ACL = user.getACL(); const ACL = user.getACL();
@@ -2131,7 +2132,7 @@ describe('Parse.User testing', () => {
let database = new Config(Parse.applicationId).database; let database = new Config(Parse.applicationId).database;
database.create('_User', { database.create('_User', {
username: '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 _auth_data_facebook: null
}, {}).then(() => { }, {}).then(() => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -2258,42 +2259,43 @@ describe('Parse.User testing', () => {
}); });
it('should fail to become user with expired token', (done) => { it('should fail to become user with expired token', (done) => {
Parse.User.signUp("auser", "somepass", null, { let token;
success: function(user) { Parse.User.signUp("auser", "somepass", null)
request.get({ .then(user => rp({
url: 'http://localhost:8378/1/classes/_Session', method: 'GET',
json: true, url: 'http://localhost:8378/1/classes/_Session',
headers: { json: true,
'X-Parse-Application-Id': 'test', headers: {
'X-Parse-Master-Key': 'test', '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)); .then(body => {
var token = body.results[0].sessionToken; var id = body.results[0].objectId;
request.put({ var expiresAt = new Date((new Date()).setYear(2015));
url: "http://localhost:8378/1/classes/_Session/" + id, token = body.results[0].sessionToken;
json: true, return rp({
headers: { method: 'PUT',
'X-Parse-Application-Id': 'test', url: "http://localhost:8378/1/classes/_Session/" + id,
'X-Parse-Master-Key': 'test', json: true,
}, headers: {
body: { 'X-Parse-Application-Id': 'test',
expiresAt: { __type: "Date", iso: expiresAt.toISOString() }, 'X-Parse-Master-Key': 'test',
}, },
}, (error, response, body) => { body: {
Parse.User.become(token) expiresAt: { __type: "Date", iso: expiresAt.toISOString() },
.then(() => { fail("Should not have succeded"); }) },
.fail((err) => { })
expect(err.code).toEqual(209); })
expect(err.message).toEqual("Session token is expired."); .then(() => Parse.User.become(token))
Parse.User.logOut() // Logout to prevent polluting CLI with messages .then(() => {
.then(done); 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) => { it('should not create extraneous session tokens', (done) => {

View File

@@ -36,8 +36,8 @@ describe('Pointer Permissions', () => {
expect(res.length).toBe(1); expect(res.length).toBe(1);
expect(res[0].id).toBe(obj.id); expect(res[0].id).toBe(obj.id);
done(); done();
}).catch((err) => { }).catch(error => {
fail('Should not fail'); fail(JSON.stringify(error));
done(); done();
}); });
}); });

View File

@@ -238,6 +238,7 @@ describe('SchemaController', () => {
}); });
Promise.all([p1,p2]) Promise.all([p1,p2])
.catch(error => { .catch(error => {
console.log(error);
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
expect(error.message).toEqual('Class NewClass already exists.'); expect(error.message).toEqual('Class NewClass already exists.');
done(); done();
@@ -693,7 +694,7 @@ describe('SchemaController', () => {
objectId: { type: 'String' }, objectId: { type: 'String' },
updatedAt: { type: 'Date' }, updatedAt: { type: 'Date' },
createdAt: { type: 'Date' }, createdAt: { type: 'Date' },
ACL: { type: 'ACL' } ACL: { type: 'ACL' },
}; };
expect(dd(schema.data.NewClass, expectedSchema)).toEqual(undefined); expect(dd(schema.data.NewClass, expectedSchema)).toEqual(undefined);
done(); done();

View File

@@ -100,12 +100,4 @@ describe('Uniqueness', function() {
done(); done();
}); });
}); });
it('adding a unique index to an existing field works even if it has nulls', done => {
});
it('adding a unique index to an existing field doesnt prevent you from adding new documents with nulls', done => {
});
}); });

View File

@@ -140,6 +140,12 @@ beforeEach(done => {
}); });
afterEach(function(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(); Parse.Cloud._removeAllHooks();
databaseAdapter.getAllClasses() databaseAdapter.getAllClasses()
.then(allSchemas => { .then(allSchemas => {
@@ -157,16 +163,7 @@ afterEach(function(done) {
}); });
}) })
.then(() => Parse.User.logOut()) .then(() => Parse.User.logOut())
.then(() => { .then(afterLogOut, afterLogOut)
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();
});
}); });
var TestObject = Parse.Object.extend({ var TestObject = Parse.Object.extend({

View File

@@ -34,6 +34,12 @@ const storageAdapterAllCollections = mongoAdapter => {
}); });
} }
const convertParseSchemaToMongoSchema = ({...schema}) => {
delete schema.fields._rperm;
delete schema.fields._wperm;
return schema;
}
export class MongoStorageAdapter { export class MongoStorageAdapter {
// Private // Private
_uri: string; _uri: string;
@@ -97,6 +103,7 @@ export class MongoStorageAdapter {
} }
createClass(className, schema) { createClass(className, schema) {
schema = convertParseSchemaToMongoSchema(schema);
return this._schemaCollection() return this._schemaCollection()
.then(schemaCollection => schemaCollection.addSchema(className, schema.fields, schema.classLevelPermissions)); .then(schemaCollection => schemaCollection.addSchema(className, schema.fields, schema.classLevelPermissions));
} }
@@ -192,6 +199,7 @@ export class MongoStorageAdapter {
// and should infer from the type. Or maybe does need the schema for validations. Or maybe needs // 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. // the schem only for the legacy mongo format. We'll figure that out later.
createObject(className, schema, object) { createObject(className, schema, object) {
schema = convertParseSchemaToMongoSchema(schema);
const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema); const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection.insertOne(mongoObject)) .then(collection => collection.insertOne(mongoObject))
@@ -208,6 +216,7 @@ export class MongoStorageAdapter {
// If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined. // 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. // If there is some other error, reject with INTERNAL_SERVER_ERROR.
deleteObjectsByQuery(className, schema, query) { deleteObjectsByQuery(className, schema, query) {
schema = convertParseSchemaToMongoSchema(schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => { .then(collection => {
let mongoWhere = transformWhere(className, query, schema); let mongoWhere = transformWhere(className, query, schema);
@@ -225,6 +234,7 @@ export class MongoStorageAdapter {
// Apply the update to all objects that match the given Parse Query. // Apply the update to all objects that match the given Parse Query.
updateObjectsByQuery(className, schema, query, update) { updateObjectsByQuery(className, schema, query, update) {
schema = convertParseSchemaToMongoSchema(schema);
const mongoUpdate = transformUpdate(className, update, schema); const mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema); const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
@@ -234,6 +244,7 @@ export class MongoStorageAdapter {
// Atomically finds and updates an object based on query. // Atomically finds and updates an object based on query.
// Return value not currently well specified. // Return value not currently well specified.
findOneAndUpdate(className, schema, query, update) { findOneAndUpdate(className, schema, query, update) {
schema = convertParseSchemaToMongoSchema(schema);
const mongoUpdate = transformUpdate(className, update, schema); const mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema); const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
@@ -243,6 +254,7 @@ export class MongoStorageAdapter {
// Hopefully we can get rid of this. It's only used for config and hooks. // Hopefully we can get rid of this. It's only used for config and hooks.
upsertOneObject(className, schema, query, update) { upsertOneObject(className, schema, query, update) {
schema = convertParseSchemaToMongoSchema(schema);
const mongoUpdate = transformUpdate(className, update, schema); const mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema); const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
@@ -251,6 +263,7 @@ export class MongoStorageAdapter {
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
find(className, schema, query, { skip, limit, sort }) { find(className, schema, query, { skip, limit, sort }) {
schema = convertParseSchemaToMongoSchema(schema);
let mongoWhere = transformWhere(className, query, schema); let mongoWhere = transformWhere(className, query, schema);
let mongoSort = _.mapKeys(sort, (value, fieldName) => transformKey(className, fieldName, schema)); let mongoSort = _.mapKeys(sort, (value, fieldName) => transformKey(className, fieldName, schema));
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
@@ -264,6 +277,7 @@ export class MongoStorageAdapter {
// Way of determining if a field is nullable. Undefined doesn't count against uniqueness, // Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
// which is why we use sparse indexes. // which is why we use sparse indexes.
ensureUniqueness(className, schema, fieldNames) { ensureUniqueness(className, schema, fieldNames) {
schema = convertParseSchemaToMongoSchema(schema);
let indexCreationRequest = {}; let indexCreationRequest = {};
let mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema)); let mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema));
mongoFieldNames.forEach(fieldName => { mongoFieldNames.forEach(fieldName => {
@@ -287,6 +301,7 @@ export class MongoStorageAdapter {
// Executs a count. // Executs a count.
count(className, schema, query) { count(className, schema, query) {
schema = convertParseSchemaToMongoSchema(schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection.count(transformWhere(className, query, schema))); .then(collection => collection.count(transformWhere(className, query, schema)));
} }

View File

@@ -67,8 +67,8 @@ const validateQuery = query => {
} }
Object.keys(query).forEach(key => { Object.keys(query).forEach(key => {
if (query[key].$regex) { if (query && query[key] && query[key].$regex) {
if (typeof query[key].$options === 'string') {g if (typeof query[key].$options === 'string') {
if (!query[key].$options.match(/^[imxs]+$/)) { if (!query[key].$options.match(/^[imxs]+$/)) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, `Bad $options value for query: ${query[key].$options}`); throw new Parse.Error(Parse.Error.INVALID_QUERY, `Bad $options value for query: ${query[key].$options}`);
} }
@@ -764,7 +764,7 @@ DatabaseController.prototype.deleteSchema = function(className) {
}) })
.then(schema => { .then(schema => {
return this.collectionExists(className) return this.collectionExists(className)
.then(exist => this.adapter.count(className)) .then(exist => this.adapter.count(className, { fields: {} }))
.then(count => { .then(count => {
if (count > 0) { if (count > 0) {
throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`); throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`);

View File

@@ -234,6 +234,20 @@ const convertSchemaToAdapterSchema = schema => {
return schema; return schema;
} }
const convertAdapterSchemaToParseSchema = ({...schema}) => {
delete schema.fields._rperm;
delete schema.fields._wperm;
schema.fields.ACL = { type: 'ACL' };
if (schema.className === '_User') {
delete schema.fields._hashed_password;
schema.fields.password = { type: 'String' };
}
return schema;
}
const injectDefaultSchema = schema => ({ const injectDefaultSchema = schema => ({
className: schema.className, className: schema.className,
fields: { fields: {
@@ -316,6 +330,7 @@ class SchemaController {
} }
return this._dbAdapter.createClass(className, convertSchemaToAdapterSchema({ fields, classLevelPermissions, className })) return this._dbAdapter.createClass(className, convertSchemaToAdapterSchema({ fields, classLevelPermissions, className }))
.then(convertAdapterSchemaToParseSchema)
.catch(error => { .catch(error => {
if (error && error.code === Parse.Error.DUPLICATE_VALUE) { if (error && error.code === Parse.Error.DUPLICATE_VALUE) {
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`); throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`);
@@ -342,6 +357,8 @@ class SchemaController {
} }
}); });
delete existingFields._rperm;
delete existingFields._wperm;
let newSchema = buildMergedSchemaObject(existingFields, submittedFields); let newSchema = buildMergedSchemaObject(existingFields, submittedFields);
let validationError = this.validateSchemaData(className, newSchema, classLevelPermissions); let validationError = this.validateSchemaData(className, newSchema, classLevelPermissions);
if (validationError) { if (validationError) {