Merge pull request #1888 from drew-gross/finish-moving-query-logic
Finish moving query logic
This commit is contained in:
@@ -123,11 +123,9 @@ describe('InstallationsRouter', () => {
|
||||
|
||||
var router = new InstallationsRouter();
|
||||
rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
|
||||
.then(() => {
|
||||
return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
|
||||
}).then(() => {
|
||||
return router.handleFind(request);
|
||||
}).then((res) => {
|
||||
.then(() => rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest))
|
||||
.then(() => router.handleFind(request))
|
||||
.then((res) => {
|
||||
var response = res.response;
|
||||
expect(response.results.length).toEqual(2);
|
||||
expect(response.count).toEqual(2);
|
||||
|
||||
@@ -17,9 +17,6 @@ var dummySchema = {
|
||||
}
|
||||
return;
|
||||
},
|
||||
getRelationFields: function() {
|
||||
return {}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +36,7 @@ describe('parseObjectToMongoObjectForCreate', () => {
|
||||
createdAt: "2015-10-06T21:24:50.332Z",
|
||||
updatedAt: "2015-10-06T21:24:50.332Z"
|
||||
};
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
|
||||
expect(output._created_at instanceof Date).toBe(true);
|
||||
expect(output._updated_at instanceof Date).toBe(true);
|
||||
done();
|
||||
@@ -62,14 +59,14 @@ describe('parseObjectToMongoObjectForCreate', () => {
|
||||
//have __op delete in a new object. Figure out what this should actually be testing.
|
||||
notWorking('a delete op', (done) => {
|
||||
var input = {deleteMe: {__op: 'Delete'}};
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
|
||||
jequal(output, {});
|
||||
done();
|
||||
});
|
||||
|
||||
it('basic ACL', (done) => {
|
||||
var input = {ACL: {'0123': {'read': true, 'write': true}}};
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
|
||||
// This just checks that it doesn't crash, but it should check format.
|
||||
done();
|
||||
});
|
||||
@@ -124,7 +121,7 @@ describe('transformWhere', () => {
|
||||
describe('mongoObjectToParseObject', () => {
|
||||
it('built-in timestamps', (done) => {
|
||||
var input = {createdAt: new Date(), updatedAt: new Date()};
|
||||
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
|
||||
var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
|
||||
expect(typeof output.createdAt).toEqual('string');
|
||||
expect(typeof output.updatedAt).toEqual('string');
|
||||
done();
|
||||
@@ -132,7 +129,9 @@ describe('mongoObjectToParseObject', () => {
|
||||
|
||||
it('pointer', (done) => {
|
||||
var input = {_p_userPointer: '_User$123'};
|
||||
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
|
||||
var output = transform.mongoObjectToParseObject(null, input, {
|
||||
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
|
||||
});
|
||||
expect(typeof output.userPointer).toEqual('object');
|
||||
expect(output.userPointer).toEqual(
|
||||
{__type: 'Pointer', className: '_User', objectId: '123'}
|
||||
@@ -142,14 +141,18 @@ describe('mongoObjectToParseObject', () => {
|
||||
|
||||
it('null pointer', (done) => {
|
||||
var input = {_p_userPointer: null};
|
||||
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
|
||||
var output = transform.mongoObjectToParseObject(null, input, {
|
||||
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
|
||||
});
|
||||
expect(output.userPointer).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
|
||||
it('file', (done) => {
|
||||
var input = {picture: 'pic.jpg'};
|
||||
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
|
||||
var output = transform.mongoObjectToParseObject(null, input, {
|
||||
fields: { picture: { type: 'File' }},
|
||||
});
|
||||
expect(typeof output.picture).toEqual('object');
|
||||
expect(output.picture).toEqual({__type: 'File', name: 'pic.jpg'});
|
||||
done();
|
||||
@@ -157,7 +160,9 @@ describe('mongoObjectToParseObject', () => {
|
||||
|
||||
it('geopoint', (done) => {
|
||||
var input = {location: [180, -180]};
|
||||
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
|
||||
var output = transform.mongoObjectToParseObject(null, input, {
|
||||
fields: { location: { type: 'GeoPoint' }},
|
||||
});
|
||||
expect(typeof output.location).toEqual('object');
|
||||
expect(output.location).toEqual(
|
||||
{__type: 'GeoPoint', longitude: 180, latitude: -180}
|
||||
@@ -167,7 +172,9 @@ describe('mongoObjectToParseObject', () => {
|
||||
|
||||
it('nested array', (done) => {
|
||||
var input = {arr: [{_testKey: 'testValue' }]};
|
||||
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
|
||||
var output = transform.mongoObjectToParseObject(null, input, {
|
||||
fields: { arr: { type: 'Array' } },
|
||||
});
|
||||
expect(Array.isArray(output.arr)).toEqual(true);
|
||||
expect(output.arr).toEqual([{ _testKey: 'testValue'}]);
|
||||
done();
|
||||
@@ -185,7 +192,9 @@ describe('mongoObjectToParseObject', () => {
|
||||
},
|
||||
regularKey: "some data",
|
||||
}]}
|
||||
let output = transform.mongoObjectToParseObject(dummySchema, null, input);
|
||||
let output = transform.mongoObjectToParseObject(null, input, {
|
||||
fields: { array: { type: 'Array' }},
|
||||
});
|
||||
expect(dd(output, input)).toEqual(undefined);
|
||||
done();
|
||||
});
|
||||
@@ -224,7 +233,7 @@ describe('transform schema key changes', () => {
|
||||
"Kevin": { "write": true }
|
||||
}
|
||||
};
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
|
||||
expect(typeof output._rperm).toEqual('object');
|
||||
expect(typeof output._wperm).toEqual('object');
|
||||
expect(output.ACL).toBeUndefined();
|
||||
@@ -241,7 +250,7 @@ describe('transform schema key changes', () => {
|
||||
}
|
||||
};
|
||||
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input);
|
||||
var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} });
|
||||
expect(typeof output._acl).toEqual('object');
|
||||
expect(output._acl["Kevin"].w).toBeTruthy();
|
||||
expect(output._acl["Kevin"].r).toBeUndefined();
|
||||
@@ -253,7 +262,7 @@ describe('transform schema key changes', () => {
|
||||
_rperm: ["*"],
|
||||
_wperm: ["Kevin"]
|
||||
};
|
||||
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
|
||||
var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
|
||||
expect(typeof output.ACL).toEqual('object');
|
||||
expect(output._rperm).toBeUndefined();
|
||||
expect(output._wperm).toBeUndefined();
|
||||
@@ -267,7 +276,12 @@ describe('transform schema key changes', () => {
|
||||
long: mongodb.Long.fromNumber(Number.MAX_SAFE_INTEGER),
|
||||
double: new mongodb.Double(Number.MAX_VALUE)
|
||||
}
|
||||
var output = transform.mongoObjectToParseObject(dummySchema, null, input);
|
||||
var output = transform.mongoObjectToParseObject(null, input, {
|
||||
fields: {
|
||||
long: { type: 'Number' },
|
||||
double: { type: 'Number' },
|
||||
},
|
||||
});
|
||||
expect(output.long).toBe(Number.MAX_SAFE_INTEGER);
|
||||
expect(output.double).toBe(Number.MAX_VALUE);
|
||||
done();
|
||||
|
||||
@@ -1121,7 +1121,7 @@ describe('miscellaneous', function() {
|
||||
})
|
||||
});
|
||||
|
||||
it('does not change inner object key names _auth_data_something', done => {
|
||||
it('does not change inner object keys named _auth_data_something', done => {
|
||||
new Parse.Object('O').save({ innerObj: {_auth_data_facebook: 7}})
|
||||
.then(object => new Parse.Query('O').get(object.id))
|
||||
.then(object => {
|
||||
|
||||
@@ -198,11 +198,18 @@ export class MongoStorageAdapter {
|
||||
}
|
||||
|
||||
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
|
||||
// Accepts the schemaController for legacy reasons.
|
||||
find(className, query, { skip, limit, sort }, schemaController) {
|
||||
find(className, query, schema, { skip, limit, sort }) {
|
||||
let mongoWhere = this.transform.transformWhere(className, query, schema);
|
||||
let mongoSort = _.mapKeys(sort, (value, fieldName) => transform.transformKey(className, fieldName, schema));
|
||||
return this.adaptiveCollection(className)
|
||||
.then(collection => collection.find(query, { skip, limit, sort }))
|
||||
.then(objects => objects.map(object => transform.mongoObjectToParseObject(schemaController, className, object)));
|
||||
.then(collection => collection.find(mongoWhere, { skip, limit, sort: mongoSort }))
|
||||
.then(objects => objects.map(object => transform.mongoObjectToParseObject(className, object, schema)));
|
||||
}
|
||||
|
||||
// Executs a count.
|
||||
count(className, query, schema) {
|
||||
return this.adaptiveCollection(className)
|
||||
.then(collection => collection.count(transform.transformWhere(className, query, schema)));
|
||||
}
|
||||
|
||||
get transform() {
|
||||
|
||||
@@ -755,7 +755,7 @@ const nestedMongoObjectToNestedParseObject = mongoObject => {
|
||||
|
||||
// Converts from a mongo-format object to a REST-format object.
|
||||
// Does not strip out anything based on a lack of authentication.
|
||||
const mongoObjectToParseObject = (schema, className, mongoObject) => {
|
||||
const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
||||
switch(typeof mongoObject) {
|
||||
case 'string':
|
||||
case 'number':
|
||||
@@ -830,17 +830,11 @@ const mongoObjectToParseObject = (schema, className, mongoObject) => {
|
||||
|
||||
if (key.indexOf('_p_') == 0) {
|
||||
var newKey = key.substring(3);
|
||||
var expected;
|
||||
if (schema && schema.getExpectedType) {
|
||||
expected = schema.getExpectedType(className, newKey);
|
||||
}
|
||||
if (!expected) {
|
||||
log.info('transform.js',
|
||||
'Found a pointer column not in the schema, dropping it.',
|
||||
className, newKey);
|
||||
if (!schema.fields[newKey]) {
|
||||
log.info('transform.js', 'Found a pointer column not in the schema, dropping it.', className, newKey);
|
||||
break;
|
||||
}
|
||||
if (expected && expected.type !== 'Pointer') {
|
||||
if (schema.fields[newKey].type !== 'Pointer') {
|
||||
log.info('transform.js', 'Found a pointer in a non-pointer column, dropping it.', className, key);
|
||||
break;
|
||||
}
|
||||
@@ -848,8 +842,7 @@ const mongoObjectToParseObject = (schema, className, mongoObject) => {
|
||||
break;
|
||||
}
|
||||
var objData = mongoObject[key].split('$');
|
||||
var newClass = (expected ? expected.targetClass : objData[0]);
|
||||
if (objData[0] !== newClass) {
|
||||
if (objData[0] !== schema.fields[newKey].targetClass) {
|
||||
throw 'pointer to incorrect className';
|
||||
}
|
||||
restObject[newKey] = {
|
||||
@@ -861,13 +854,12 @@ const mongoObjectToParseObject = (schema, className, mongoObject) => {
|
||||
} else if (key[0] == '_' && key != '__type') {
|
||||
throw ('bad key in untransform: ' + key);
|
||||
} else {
|
||||
var expectedType = schema.getExpectedType(className, key);
|
||||
var value = mongoObject[key];
|
||||
if (expectedType && expectedType.type === 'File' && FileCoder.isValidDatabaseObject(value)) {
|
||||
if (schema.fields[key] && schema.fields[key].type === 'File' && FileCoder.isValidDatabaseObject(value)) {
|
||||
restObject[key] = FileCoder.databaseToJSON(value);
|
||||
break;
|
||||
}
|
||||
if (expectedType && expectedType.type === 'GeoPoint' && GeoPointCoder.isValidDatabaseObject(value)) {
|
||||
if (schema.fields[key] && schema.fields[key].type === 'GeoPoint' && GeoPointCoder.isValidDatabaseObject(value)) {
|
||||
restObject[key] = GeoPointCoder.databaseToJSON(value);
|
||||
break;
|
||||
}
|
||||
@@ -876,7 +868,16 @@ const mongoObjectToParseObject = (schema, className, mongoObject) => {
|
||||
}
|
||||
}
|
||||
|
||||
return { ...restObject, ...schema.getRelationFields(className) };
|
||||
const relationFieldNames = Object.keys(schema.fields).filter(fieldName => schema.fields[fieldName].type === 'Relation');
|
||||
let relationFields = {};
|
||||
relationFieldNames.forEach(relationFieldName => {
|
||||
relationFields[relationFieldName] = {
|
||||
__type: 'Relation',
|
||||
className: schema.fields[relationFieldName].targetClass,
|
||||
}
|
||||
});
|
||||
|
||||
return { ...restObject, ...relationFields };
|
||||
default:
|
||||
throw 'unknown js type';
|
||||
}
|
||||
|
||||
@@ -628,16 +628,9 @@ DatabaseController.prototype.find = function(className, query, {
|
||||
skip,
|
||||
limit,
|
||||
acl,
|
||||
sort,
|
||||
sort = {},
|
||||
count,
|
||||
} = {}) {
|
||||
let mongoOptions = {};
|
||||
if (skip) {
|
||||
mongoOptions.skip = skip;
|
||||
}
|
||||
if (limit) {
|
||||
mongoOptions.limit = limit;
|
||||
}
|
||||
let isMaster = acl === undefined;
|
||||
let aclGroup = acl || [];
|
||||
let op = typeof query.objectId == 'string' && Object.keys(query).length === 1 ? 'get' : 'find';
|
||||
@@ -653,34 +646,29 @@ DatabaseController.prototype.find = function(className, query, {
|
||||
throw error;
|
||||
})
|
||||
.then(schema => {
|
||||
if (sort) {
|
||||
mongoOptions.sort = {};
|
||||
for (let fieldName in sort) {
|
||||
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt,
|
||||
// so duplicate that behaviour here.
|
||||
if (fieldName === '_created_at') {
|
||||
fieldName = 'createdAt';
|
||||
sort['createdAt'] = sort['_created_at'];
|
||||
} else if (fieldName === '_updated_at') {
|
||||
fieldName = 'updatedAt';
|
||||
sort['updatedAt'] = sort['_updated_at'];
|
||||
}
|
||||
|
||||
if (!SchemaController.fieldNameIsValid(fieldName)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
|
||||
}
|
||||
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`);
|
||||
}
|
||||
const mongoKey = this.transform.transformKey(className, fieldName, schema);
|
||||
mongoOptions.sort[mongoKey] = sort[fieldName];
|
||||
}
|
||||
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt,
|
||||
// so duplicate that behaviour here. If both are specified, the corrent behaviour to match Parse.com is to
|
||||
// use the one that appears first in the sort list.
|
||||
if (sort._created_at) {
|
||||
sort.createdAt = sort._created_at;
|
||||
delete sort._created_at;
|
||||
}
|
||||
if (sort._updated_at) {
|
||||
sort.updatedAt = sort._updated_at;
|
||||
delete sort._updated_at;
|
||||
}
|
||||
Object.keys(sort).forEach(fieldName => {
|
||||
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`);
|
||||
}
|
||||
if (!SchemaController.fieldNameIsValid(fieldName)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
|
||||
}
|
||||
});
|
||||
return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, op))
|
||||
.then(() => this.reduceRelationKeys(className, query))
|
||||
.then(() => this.reduceInRelation(className, query, schemaController))
|
||||
.then(() => this.adapter.adaptiveCollection(className))
|
||||
.then(collection => {
|
||||
.then(() => {
|
||||
if (!isMaster) {
|
||||
query = this.addPointerPermissions(schemaController, className, op, query, aclGroup);
|
||||
}
|
||||
@@ -696,12 +684,10 @@ DatabaseController.prototype.find = function(className, query, {
|
||||
query = addReadACL(query, aclGroup);
|
||||
}
|
||||
validateQuery(query);
|
||||
let mongoWhere = this.transform.transformWhere(className, query, schema);
|
||||
if (count) {
|
||||
delete mongoOptions.limit;
|
||||
return collection.count(mongoWhere, mongoOptions);
|
||||
return this.adapter.count(className, query, schema);
|
||||
} else {
|
||||
return this.adapter.find(className, mongoWhere, mongoOptions, schemaController)
|
||||
return this.adapter.find(className, query, schema, { skip, limit, sort })
|
||||
.then(objects => objects.map(object => filterSensitiveData(isMaster, aclGroup, className, object)));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -686,23 +686,6 @@ class SchemaController {
|
||||
hasClass(className) {
|
||||
return this.reloadData().then(() => !!(this.data[className]));
|
||||
}
|
||||
|
||||
getRelationFields(className) {
|
||||
if (this.data && this.data[className]) {
|
||||
let classData = this.data[className];
|
||||
return Object.keys(classData).filter((field) => {
|
||||
return classData[field].type === 'Relation';
|
||||
}).reduce((memo, field) => {
|
||||
let type = classData[field];
|
||||
memo[field] = {
|
||||
__type: 'Relation',
|
||||
className: type.targetClass
|
||||
};
|
||||
return memo;
|
||||
}, {});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a promise for a new Schema.
|
||||
|
||||
Reference in New Issue
Block a user